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

Bug: Scripts injected by browser extensions (e.g. React dev tools) may cause hydration errors #25924

Closed
azangru opened this issue Dec 22, 2022 · 1 comment · Fixed by #26233
Closed
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug

Comments

@azangru
Copy link

azangru commented Dec 22, 2022

My apologies if this is the wrong place to report the below; but I could not find a more appropriate place to get feedback from the React team. It seems that the below is going to be a common use case.

React version: 18.2.0

Description of the problem

Since I've started using the renderToPipeableStream api making React render the whole DOM tree starting from the html tag (inspired by Dan's demo; but now I can see a similar technique described in the beta docs), I've been seeing occasional hydration errors popping up in our error tracker. Since the html payload that I am rendering server-side is minuscule and limited to just the contents of the head tag, I was very puzzled as to the cause of the error. Even more puzzling was that the error occurred only occasionally, which made the diagnosis of the cause difficult. The fact that the production build of React is not even making it obvious which elements caused the mismatch made it harder still.

After a long time spent in debugging, I discovered that at least one possible cause of the hydration error is the script injected by react-devtools chrome extension:

Screenshot 1: React (in dev build) complaining about an unexpected script tag:

image

Screenshot 2: Debugger paused in the function that logs out the warning. The offending child node is a script tag.

image

Screenshot 3: The unexpected script tag is revealed to be injected by React devtools chrome extension to request react-devtools backend:

image

Screenshot 4: The script tag is injected after the body tag:

image

This raises several questions:

  • In case of React devtools, should it add the suppress-hydration-warning attribute (an escape hatch suggested in the docs) to the script added by the chrome extension? Will it help if it did?
  • Similar problems might potentially occur when using third-party scripts that modify the DOM (e.g. add script or style elements to the DOM). Another common example is webpack splitting CSS into small chunks and then adding link tags for the chunks into the head dynamically during client-side navigation. Is this a concern for situations when React is responsible for the whole html tree? Is there any advice from the React team regarding this?
  • It looks like the recommended SSR option, according to the new docs, is renderToPipeableStream/renderToReadableStream, with React being in control of the whole html element. If third-party scripts are indeed a concern when React controls of the whole DOM tree, then what is the recommended api for server-side rendering? Perhaps this should be addressed in the docs?
@azangru azangru added the Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug label Dec 22, 2022
@azangru
Copy link
Author

azangru commented Feb 19, 2023

Another debugging session of an unexpected behaviour in the development build (the page always was broken during the first load in a new browser tab, although always was fine upon refresh) revealed the same root cause — React dev tools chrome extension was injecting a script tag into the head of the document, which React was not expecting. In my case, that resulted in an irrecoverable hydration error — React got so confused that it called my component without passing it any of the props specified in the JSX, which was very difficult to debug:

Figure 1: the first child of the html element is a script tag injected by React dev tools:
image

Figure 2: React expected the first child of the html element to be head, as specified in the JSX:
image

mondaychen added a commit that referenced this issue Feb 24, 2023
Fixes #25924 for React DevTools
specifically.

## Summary

If we remove the script after it's loaded, it creates a race condition
with other code. If some other code is searching for the first script
tag or first element of the document, this might broke it.

## How did you test this change?

I've tested in my local build that even if we remove the script tag
immediately, the code is still correctly executed.
github-actions bot pushed a commit that referenced this issue Feb 24, 2023
Fixes #25924 for React DevTools
specifically.

## Summary

If we remove the script after it's loaded, it creates a race condition
with other code. If some other code is searching for the first script
tag or first element of the document, this might broke it.

## How did you test this change?

I've tested in my local build that even if we remove the script tag
immediately, the code is still correctly executed.

DiffTrain build for [5641660](5641660)
jerrydev0927 added a commit to jerrydev0927/react that referenced this issue Jan 5, 2024
Fixes facebook/react#25924 for React DevTools
specifically.

## Summary

If we remove the script after it's loaded, it creates a race condition
with other code. If some other code is searching for the first script
tag or first element of the document, this might broke it.

## How did you test this change?

I've tested in my local build that even if we remove the script tag
immediately, the code is still correctly executed.

DiffTrain build for [564166099b5f46dd33f3356b01a72c0314103a18](facebook/react@5641660)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant