react-helmet icon indicating copy to clipboard operation
react-helmet copied to clipboard

Title update lag

Open alukach opened this issue 7 years ago • 26 comments

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?

alukach avatar Nov 05 '16 05:11 alukach

@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.

cwelch5 avatar Dec 05 '16 22:12 cwelch5

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...

MaffooBristol avatar Apr 10 '17 18:04 MaffooBristol

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.

dpehrson avatar May 01 '17 15:05 dpehrson

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

This change https://github.com/nfl/react-helmet/commit/b22e2f4075971f8b80568cd9911a2143f602add1 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.

adam187 avatar Aug 21 '17 09:08 adam187

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.

JimmyMultani avatar Sep 19 '17 19:09 JimmyMultani

@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);
    };
}

daveschumaker avatar Oct 12 '17 22:10 daveschumaker

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.

kevinfodness avatar Jan 09 '18 04:01 kevinfodness

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.

yangshun avatar Jan 10 '18 05:01 yangshun

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

rossPatton avatar May 17 '18 21:05 rossPatton

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

PMustard avatar Nov 07 '18 15:11 PMustard

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??

PMustard avatar Nov 08 '18 12:11 PMustard

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 avatar Nov 08 '18 17:11 tmbtech

@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 :)

PMustard avatar Nov 09 '18 09:11 PMustard

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

krismeister avatar Feb 14 '19 23:02 krismeister

what happened to this issue?

donjo9 avatar May 06 '19 20:05 donjo9

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.

abstractvector avatar Aug 11 '19 14:08 abstractvector

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

acroix avatar Oct 15 '19 13:10 acroix

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

dcripplinger avatar Oct 21 '19 16:10 dcripplinger

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

vuk-niko-77 avatar Nov 15 '19 11:11 vuk-niko-77

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

missbruni avatar Mar 03 '20 16:03 missbruni

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;
}

marlomgirardi avatar May 01 '20 08:05 marlomgirardi

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.

corneliugaina avatar Sep 25 '20 14:09 corneliugaina

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

Ekendahl avatar May 20 '21 12:05 Ekendahl

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?

vazkir avatar Jun 07 '21 14:06 vazkir

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

caleyshemc avatar Apr 21 '22 02:04 caleyshemc

BUMP!

DjVreditel avatar Nov 20 '23 11:11 DjVreditel