Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<Head><title> not set by the time history change event fires #6025

Closed
dandrei opened this issue Jan 10, 2019 · 14 comments · Fixed by #14132
Closed

<Head><title> not set by the time history change event fires #6025

dandrei opened this issue Jan 10, 2019 · 14 comments · Fixed by #14132
Assignees
Milestone

Comments

@dandrei
Copy link

dandrei commented Jan 10, 2019

Bug report

Describe the bug

I'm using Google Tag Manager with a custom trigger for history change (inspired by SO).

The problem is that the content of the <title> tag as reported in GTM at the time the history change event fires is inconsistent: it may be set to the correct value, to the previous page's title, or it may even be "not set" entirely.

I set the title with the typical JSX:

import Head from "next/head";
/* ... */
<Head><title>stuff</title></Head>

Issue #4044 that was reported for Next.js v5.0.0. may be related.

Expected behavior

<title> should have the right value by the time the history change event fires.

System information

  • Version of Next.js: 7.0.2
  • React version: 16.7.0-alpha.2 (using Hooks. Could this be the issue?)

Is there a way to solve this?

Alternatively, is there a way to force server rendering every time, so as to bypass all client-side routing / dynamic rendering entirely?

@dandrei
Copy link
Author

dandrei commented Jan 16, 2019

I also checked the value of document.title on Router.events.on('routeChangeComplete'). The inconsistency is still there.

@dandrei
Copy link
Author

dandrei commented Feb 12, 2019

I upgraded to Next.js 8 (stable) and using React 16.8.

The issue is still present.

@dandrei
Copy link
Author

dandrei commented Feb 13, 2019

@HaNdTriX I see you mentioned the same issue in PR 4919. Has there been any progress? Thanks.

@HaNdTriX
Copy link
Contributor

I am afraid not. Nevertheless I am currently not tracking the progress of this project on a daily basis.

@dandrei
Copy link
Author

dandrei commented Feb 14, 2019

Thanks, @HaNdTriX. Can you think of a way for me to hack/force the update before the event fires, until the bug itself is fixed in Next.js?

@HaNdTriX
Copy link
Contributor

HaNdTriX commented Feb 14, 2019

yepp. Implement it yourself using react (componentDidMount/useEffect[]):

components/OnRouteChange.js

import React, { useEffect } from 'react'
import PropTypes from 'prop-types'
import { withRouter } from 'next/router'

const OnRouteChange = ({ children, router, onComplete }) => {
  useEffect(() => {
    onComplete(router)
  }, [router.pathname])
  return children
}

OnRouteChange.propTypes = {
  children: PropTypes.node,
  router: PropTypes.object.isRequired,
  onComplete: PropTypes.func.isRequired
}

export default withRouter(OnRouteChange)

pages/_app.js (Usage example)

import React from 'react'
import App, { Container } from 'next/app'
import OnRouteChange from '../components/OnRouteChange'

export default class MyApp extends App {
  static async getInitialProps({ Component, router, ctx }) {
    let pageProps = {}

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx)
    }

    return { pageProps }
  }

  render () {
    const { Component, pageProps } = this.props
    return (
      <OnRouteChange onComplete={(router) => {
        console.log('Route has changed', router.pathname)
      }}>
        <Container>
          <Component {...pageProps} />
        </Container>
      </OnRouteChange>
    )
  }
}

Future (Standalone Hook)

In the future we could probably implement this as a standalone hook.

import { useRouter } from 'next/router'

function useRouteChange (callback) {
  const router = useRouter()
  useEffect(() => callback, [router.pathname])
}

export default useRouteChange

@dandrei
Copy link
Author

dandrei commented Feb 14, 2019

Thanks @HaNdTriX for the code, but I'm afraid the problem still persists.

e.g. even here:

<OnRouteChange onComplete={(router) => {
    console.log(`Route has changed: ${router.pathname} Title: [${document.title}]`);
}}>

I still get mismatches.

Route has changed: / Title: []

Seems <Head> is continuously lagging behind.

@WillSelway
Copy link

WillSelway commented Feb 26, 2019

@dandrei

We have the same issue however we fixed it using a combination of the above suggestions, setTimeout and using Google Tag Manager to map some data into Google Analytics. Wrapping the event in a setTimeout gives NextJS time to run the componentDidMount and setup the properly.

useEffect(() => {
  setTimeout(() => {
    if(!window) return;

    window.dataLayer.push({
      'event': 'VirtualPageView',
      'path': router.asPath, // This is not needed, but could be passed through and used in GTM
      'title': document.title
    })
  }, 0);
}, [router.asPath]);

Based on the variable and event naming in my example, in tag manager, if you setup a new Tag on a custom event of 'VirtualPageView' with a 'Tag Type' of 'Google Analytics - Universal Analytics'.
Set 'Enable overriding settings in this tag' to enabled.
Add to 'Fields to Set', 'Field Name' -> Title, Value -> {{ title }} (note you will need to configure {{ title }} as a dataLayer variable in GTM as well)

@timneutkens
Copy link
Member

I think this might be solved once we change next/head to use the useEffect hook 🤔

@StarpTech
Copy link
Contributor

Accessing document.title inside a timeout in routeChangeComplete works as a workaround.

@Timer Timer modified the milestones: 9.0.3, 9.0.x Jul 18, 2019
@Timer Timer modified the milestones: 9.0.x, backlog Aug 30, 2019
@peduarte
Copy link

Similarly to @StarpTech I've worked around it by wrapping it in requestAnimationFrame

Will be watching this issue to remove the workaround once it's sorted.

Cheers!

@jackocnr
Copy link

jackocnr commented Apr 7, 2020

@WillSelway I'm also using GTM to send pageviews to GA. Thanks for the setTimeout trick - that fixed it for me.

For anyone else trying to send pageviews to GA through GTM, I just thought I'd add that with the setTimeout trick, you don't actually need to send the page path or the title, because at that point it has updated in the browser, so the GA tag will pick it up like normal.

@nx-giap
Copy link

nx-giap commented Apr 15, 2020

Thank @WillSelway and @jackocnr for greate comment.
I fixed it according to the example code below:

gtag.js

export const GA_TRACKING_ID = '<YOUR_GA_TRACKING_ID>'

 export const pageview = url => {
  window.gtag('config', GA_TRACKING_ID, {
     page_path: url,
   })
 }

pages/_app.js

import { useRouter } from "next/router";
import * as gtag from 'src/lib/gtag'

// For the purpose of using function component hooks
function MyComponent({ children }) {
  const router = useRouter()

  React.useEffect(() => {
    setTimeout(() => {
      if (!window) {
        return
      }

      gtag.pageview(router.asPath)
    }, 0)
  }, [router.asPath])

  return <>{children}</> // The fragment is just illustrational
}

class CustomApp extends App {

  render() {
    const { Component, pageProps } = this.props

    return (
      <MyComponent>
        {Component ? <Component {...pageProps} /> : <div />}
      </MyComponent>
    )
  }
}

export default CustomApp

@kodiakhq kodiakhq bot closed this as completed in #14132 Jun 12, 2020
@Timer Timer modified the milestones: backlog, june 2020 Jun 12, 2020
kodiakhq bot pushed a commit that referenced this issue Jun 12, 2020
This adds a test case for the `document.title` not being updated by the time `routeChangeComplete` is fired. This should have been made consistent with #13287 so should be able to be closed now

Closes: #6025
rokinsky pushed a commit to rokinsky/next.js that referenced this issue Jul 11, 2020
This adds a test case for the `document.title` not being updated by the time `routeChangeComplete` is fired. This should have been made consistent with vercel#13287 so should be able to be closed now

Closes: vercel#6025
@balazsorban44
Copy link
Member

This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@vercel vercel locked as resolved and limited conversation to collaborators Jan 30, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.