-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Implement workaround for hydration error on React strict mode #3637
Conversation
b1c4f4d
to
8f116e3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much for starting this!
Co-authored-by: Robert Snow <snowystinger@gmail.com>
I'll update |
- remove implementation details - mention `identifierPrefix`
Any idea why the original implementation doesn't work in strict mode? Is the Switching to |
Maybe possible with a ref. Something like this? function useId() {
// ...
let ref = useRef(null);
if (ref.current == null) {
ref.current = "react-aria-" + ctx.current++ // ...
}
return ref.current
} (Typed on mobile, sorry for any mistakes) |
I see. I think that idea would work well on React 17, but would cause hydration errors in concurrent mode on React 18, where the client and the server may not render components in the same order. |
I believe @devongovett do you have any ideas? |
I believe that is only a problem when using the brand new streaming server renderer, which is only for Server Components not traditional SSR. Also we still support React 16 and 17, so we should fix the issue there as well. I think the approach I posted above might work for 16, 17, and regular SSR in 18. We would need to switch to React.useId for Server Components support at some point, but I'm concerned that there will be side effects from such a change. React Aria's |
Thanks for the explanation. That makes sense. May I use the approach you posted above to check if it works? I would create a new pull request if successful. |
Sure, that would be great thanks! |
24e36e9
to
925383e
Compare
I just tried the This behavior doesn't seem to be documented clearly, but is described in facebook/react#22872 and facebook/react#18003 (comment). |
Hmm, this pattern is shown in the docs here: https://reactjs.org/docs/hooks-faq.html#how-to-create-expensive-objects-lazily |
Although the docs implies https://codesandbox.io/s/loving-swirles-zc254v?file=/src/index.js import { useRef, StrictMode } from "react";
import { createRoot } from "react-dom/client";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<App />
</StrictMode>
);
let cnt = 0;
function App() {
const ref = useRef(null);
if (ref.current === null) {
ref.current = `id-${cnt++}`;
}
console.log(ref.current);
return ref.current;
} |
@ciffelia: is the issue that the server-side correctly generates just one ID, and the client-side generates two? Maybe this is a "if you can't beat them, join them" situation: could modify the server-side to first generate a throw-away ID, then generate one that gets assigned? I guess ideally we'd want to also check if strict mode is disable before generating the first ID, if that's possible in the server-side (I'm assuming it is). |
@LucasUnplugged I don't think that's possible. The SSR server sends rendered HTML to a browser immediately after the first rendering completed. There's no way to generate IDs after the first render. |
What I mean is, it sounds like the frontend runs the ID generation code twice, so the first instance has ID 2 in the frontend and ID 1 in SSR code. The second has ID 4 instead of ID 2, and so on. So in that case, I'm suggesting the ID generation func be run twice in the SSR logic as well, like: genID(); // Throw away
const actualID = genID; // Save this one If I understood correctly, the mismatch in IDs is deterministic, in that the first ID is always the same; the second is always the same; etc. The only difference is the frontend is doubling them up, in strict mode. |
@LucasUnplugged I was mistaken about the behavior of Strict Mode. Indeed, I think the idea you presented is possible. Unfortunately it seems to be impossible to check if the strict mode is enabled (at least in the render phase), so we need to add an option to enable that feature: <StrictMode>
<SSRProvider strictMode={true}>
<App />
</SSRProvider>
</StrictMode> Apart from that, I came up with an idea that allow users to let react-aria use import React from 'react'
<SSRProvider useId={React.useId}>
<App />
</SSRProvider> This will not only solve the problem with strict mode, but also ensure that our Since both features are optional, there is less risk of breaking things. Those who are using React 18 may enable the latter feature, and others may choose the former. I would like to hear from maintainers before implementing them. |
Remix shipped their |
This commit implements a workaround for hydration error during SSR running in strict mode (#2231, #779).
The workaround uses
React.useId
, which is introduced in React 18. On React 16 and 17, nothing changes with this commit (i.e. hydration error still occurs).✅ Pull Request Checklist:
📝 Test Instructions:
Note: I'm not sure if this test runs in react strict mode.
🧢 Your Project:
pixiv/charcoal