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

Title update lag #189

Open
alukach opened this issue Nov 5, 2016 · 26 comments
Open

Title update lag #189

alukach opened this issue Nov 5, 2016 · 26 comments

Comments

@alukach
Copy link

alukach commented Nov 5, 2016

I've noticed that there's a bit of a lag between a route changing and the title property updating. I'm guessing that this is around 200 - 300ms. While minor, it does look just a bit off. I experience this in both my project and also running the react-helmet-example. Are there any suggestions about reducing this lag? If not, is there any known faster way to update the document's title?

@cwelch5
Copy link
Contributor

cwelch5 commented Dec 5, 2016

@alukach if your component tree is rather deep, then that could cause more delay, but in general we are at the mercy of the DOM's update latency. Although, we are due for some performance metrics for Helmet because we may be able to optimize more. I'll mark this as a todo to measure performance and see if there's a way to optimize.

@MaffooBristol
Copy link

It also seems to be on render, but if it were before mount I'd reckon it'd be quicker. My document tree is very small but I'm still seeing a noticeable delay.

Admittedly though, I did try it with document.title = xyz in componentWillMount so perhaps it's not the fault of this module at all...

@dpehrson
Copy link

dpehrson commented May 1, 2017

This is probably out of the scope of this library, but I thought I'd pop in a note of where this is problematic: Analytics.

I am sending Google Tag Manager events on history change, and the events getting sent to Google Analytics are always off-by-one on the URL <-> Browser Title relationship.

Hooks I've tried in my setup:

  1. history.listen(): Fires before the title is updated
  2. <Route onChange/>: Fires before the title is updated
  3. componentDidUpdate(): Fires before the title is updated

The only hook I've found where I can get the correct timing is in <Helmet onChangeClientState/> but I then need to coordinate that with other callbacks to ensure analytics calls are made only at the right times.

To note again: I don't actually think this is helmet's problem, but I probably won't be the last person to hit a wall here and wanted to leave some information behind.

@adam187
Copy link

adam187 commented Aug 21, 2017

I had same issue.
It looks to me that this delay is caused by adding support for requestIdleCallback in version 5.

This change b22e2f4 should also resolve this issue because it will be possible to disabling requestIdleCallback by using defer={false} prop.

But for now downgrading to 4.0.0 solved issue for me.

@JimmyMultani
Copy link

JimmyMultani commented Sep 19, 2017

I'm running into this exact issue with PIwik Analytics as well. It will capture the previous document title since it triggers on history change.

I'm hoping that what @adam187 said really does fix this issue.

@daveschumaker
Copy link

daveschumaker commented Oct 12, 2017

@dpehrson Ah, hah! I'm so glad you posted about this with regard to analytics problems.

We recently upgraded from react-helmet v3.2.3 to v5.2.0 in preparation for React v16 and noticed some funky off-by-one-page issues with the tracking events we send to Google Analytics.

For now, we temporarily resolved it by wrapping our tracking functions inside a setTimeout call with a delay of 0 ms -- this ends up forcing the included function to run after the current event loop is completed:

trackCurrentPage() {
    return (dispatch) => {
        setTimeout(() => {
            dispatch(AnalyticsActions.track.resetPageCustomVars());
            dispatch(AnalyticsActions.track.deviceType());
            dispatch(AnalyticsActions.track.user());
            dispatch(AnalyticsActions.sendPageViewToGA());
        }, 0);
    };
}

@kevinfodness
Copy link

I solved this by adding a variable to my component class called shouldTrackPageview that gets set to true in componentDidUpdate, and the value is checked in the callback for onChangeClientState. If the value is true, then it sets the value to false (to prevent double-firing the event) and then it executes the logic to send the pageview to Google Analytics.

I tried the defer method mentioned above, but it didn't solve the issue for me.

@yangshun
Copy link

defer didn't really work for me too. I was using autotrack so my URL changes were handled automatically. This was what I did to get it to work for me:

// Added this hack:
window.gaplugins.UrlChangeTracker.prototype.handleUrlChange = function(historyDidUpdate) {
  window.requestIdleCallback(() => {
    setTimeout(() => {
      origHandleUrlChange.call(this, historyDidUpdate);
    }, 100);
  });
};

window.ga('create', config.googleAnalyticsId, 'auto');

window.ga('require', 'eventTracker');
window.ga('require', 'outboundLinkTracker');
window.ga('require', 'urlChangeTracker');

window.ga('send', 'pageview');

In fact, UrlChangeTracker.handleUrlChange()'s implementation is already made async via setTimeout(..., 0) but it's still insufficient. A timeout of 100ms delays its invocation sufficiently long enough for React to do its stuff.

Why 100ms? It's an arbitary value actually. It's short enough to not miss any page change event, but long enough for React to complete its rendering. This works well enough for most cases.

@rossPatton
Copy link

rossPatton commented May 17, 2018

Seeing something similar to this problem, but not as a result of analytics.

I have a script I run locally against my site. This script hits all key pages in the sitemap, as well a few odd cases (301s, 404s, 400s, and 500s). We run this script on every commit (against a baseline) to ensure we have no SEO regressions or unexpected 500s or what have you

After switching to react-helmet, the script is off-by-one. What i mean by this is that no page when tested individually returns incorrect tags, but when tested as a group, I often get undefined back on the first test, with each subsequent test being behind 1 url behind (so that the 2nd test returns the correct seo tags for the 1st test, which obviously fails the 2nd test). If there is some inexplicable intentional ms delay, then I guess this is to be expected, but it doesn't make any sense to me. All my script does is hit a url, get the html back, and then uses cheerio to scrape the page and compare against the baseline

Why would react-helmet be set up this way? Seems like it would be a disaster for any SEO crawler

@PMustard
Copy link

PMustard commented Nov 7, 2018

Hi, I got this issue as well at the moment. Is there any good or correct solution ??

@PMustard
Copy link

PMustard commented Nov 8, 2018

Downdreading to version 4 when using React 16 created errors with prototypes. And after changing this to see if this helps created new errors.

I see that this issue is quite popular and people are moving away from Helmet just to sort this issue.

Are you planning to address this issue?? Cause if not I will need to use something else as well??

@tmbtech tmbtech added bug and removed enhancement labels Nov 8, 2018
@tmbtech
Copy link
Contributor

tmbtech commented Nov 8, 2018

Removing the enhancement tag and escalating to a bug. We are in the process of discussing the roadmap for the next update major update and will be addressing some of these bugs.

@tmbtech tmbtech self-assigned this Nov 8, 2018
@PMustard
Copy link

PMustard commented Nov 9, 2018

@tmbtech Hi. Thanks for the quick replay. I think this is a major disadvantage of your class. Everyone Is now want to have Analytics and Google stuff connected right for their marketing purposes and this puts your class back in the pack but it works ok for any other purpose.

I think the solution to this one will somehow connect it to the Router and apply changes to head before the path will change. I know this sounds weird but I think this will prevent for having head data behind a change of URL for sure.

I will love to see this done and especially you did not have any publish for a year according to npm and you still have crazy numbers of downloads weekly (~300k). I think this puts you guys in responsibility to keep this up to date. Especially that react is growing fast.

Regards and I will check up on this issue :)

Thanks for promoting this and pushing this forwards :) Keep up the good work :)

@krismeister
Copy link

Alot of overkill fixes on this thread, you can set the title tag easily through window.document.title and its immediate.

@donjo9
Copy link

donjo9 commented May 6, 2019

what happened to this issue?

@abstractvector
Copy link

Reading through the solutions above, none were quite right for what I needed. An arbitrary delay is insufficient, as for us the page title is dependent on the results of an asynchronous API call.

I'm using react-helmet on every page to change various data in the header, including the title. I stopped using react-piwik's router connection, and instead use a manually invoked function. The component below is rendered within the application, at the root level. I have simplified the code below to include only what is necessary. Hope it helps someone.

import React from 'react';
import Piwik from 'react-piwik';
import { Helmet } from 'react-helmet';

new Piwik({
  url: 'https://analytics.example.com', // insert your analytics URL here
  siteId: 1,
  trackErrors: true,
});

Piwik.push(['enableHeartBeatTimer']);

let lastPath = null;

const onHelmetChange = ({ metaTags }) => {
  if (
    'undefined' !== typeof window &&
    lastPath !== window.location.pathname &&
    metaTags.filter(m => m.name === 'page-loaded' && m.content === 'true').length > 0
  ) {
    if (lastPath !== null) {
      Piwik.push(['setReferrerUrl', `${window.location.origin}${lastPath}`]);
    }

    Piwik.push(['setDocumentTitle', document.title]);
    Piwik.push(['setCustomUrl', window.location.href]);
    Piwik.push(['trackPageView']);

    lastPath = window.location.pathname;
  }
}

export default () => (
  <Helmet onChangeClientState={onHelmetChange}>
    <meta name="page-loaded" content="false" />
  </Helmet>
)

then on each page, when I set the metadata using Helmet, I also set page-loaded to true, like the following:

<Helmet>
  <meta name="page-loaded" content="true" />
</Helmet>

The same approach can be used to trigger Google Analytics or any other events on true page load. Make sure you set page-loaded to true on each page else it won't work - in our architecture, this was a simple solution. This works because Helmet switches back to its default between page loads, hence resetting page-loaded to false until the next page is rendered.

@acroix
Copy link

acroix commented Oct 15, 2019

Did someone resolved this issues using react-tag-manger ?
I would like to have some feedback and maybe an example

@dcripplinger
Copy link

I'm having the same problem with the document title sent out in analytics lagging one behind.

@vuk-niko-77
Copy link

Still the same problem in google analytics. Any news about this bug ? Thanks

@missbruni
Copy link

We are still having issues with this, title always showing one behind from url on GA. Any news on this please?

@marlomgirardi
Copy link

marlomgirardi commented May 1, 2020

I've tried for a bit to think in a good solution, but there are only two solutions that I can think of:

  1. A package on top of the react-helmet should be created as a combination with GA.
  2. Use something like react-ga, but either way, some work would be needed because to send the tracking it would need to wait for the title change on page change.

As I'm a bit out of schedule, for now, my solution was to use an abstraction that I had for SEO with ReactHelmet to manage the tracking of all pages.

When onChangeClientState is triggered:

if (prevPathname !== window.location.pathname) {
  analytics.onPageChange();
  prevPathname = window.location.pathname;
}

@corneliugaina
Copy link

To handle this, in my useEffect I gave a setTimeout of ~750-1000ms to execute ReactGA.pageview(), like :

    useEffect(() => {
        setTimeout(() => {
            ReactGA.pageview(window.location.pathname);
        }, 1000);
    })

However, I'm still looking for a cleaner solution.

@Ekendahl
Copy link

I'm having the same problem when implementing Segment tracking on a React SPA.

@vazkir
Copy link

vazkir commented Jun 7, 2021

Removing the enhancement tag and escalating to a bug. We are in the process of discussing the roadmap for the next update major update and will be addressing some of these bugs.

Is there any update on this?

@caleyshemc
Copy link

This is still a major issue that could lead to my project migrating away from react-helmet.

@DjVreditel
Copy link

BUMP!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests