React Adaptive Hooks will cause attribute mismatch during server side rendering
See original GitHub issueHi. I saw a recent issue #2 regarding server side rendering. However, this behaviour simply hides the attribute mismatch that can happen due to the SSR phase and the client phase rendering differently.
I have built an example repo that showcases this mismatch. https://github.com/gja/react-adaptive-hooks-ssr
Apologies for just copying the relevant file (react-adaptive-hooks/network), but I didn’t have time to configure babel and whatnot for server side rendering.
Usage to start repo:
npm install
# npx webpack (if you want to recompile main.js, though it's checked in)
node .
As you can see from https://github.com/gja/react-adaptive-hooks-ssr/blob/master/src/ReactAdaptiveHooksExample.js, it should render a paragraph whose text and class are matching (ie: <p class="4g">Your Network Status Is 4g</p>
). However, the ssr hydration will not resolve the classname (ie: <p class="unsupported">Your Network Status Is 4g</p>
).
However, react hydrate does not go through element attributes to look for mismatches, hence these attributes will never be caught. (worse, react believes that the classname is set to 4g, while the dom has a different value, so this will not be rectified in future renders as well).
I’m not sure what the supported model is here. In the old class world, i’d have only called out to the new functions during componentDidMount, and change the behaviour that way. I’m not sure what the equivalent is in the react hooks world.
Maybe return undefined when loading, then useEffect to set loading to false, and return the correct value on the next render.
Issue Analytics
- State:
- Created 4 years ago
- Comments:13 (6 by maintainers)
Top GitHub Comments
Another option here would be to export a context provider from the module that enables/disables a “hydration mode”. It could switch hooks to provide initial unknown values, then use useEffect or a setState callback to determine when hydration has completed and re-run any hooks that need client-side-specific output.
My guess is that emulating the server’s “unknown” state would be relatively straightforward since that codepath already exists for SSR.
h/t @devknoll for the context suggestion, which is much better than my initial idea of a singleton module with a global isHydrating flag.
@gja
It does not fix the mismatch issue, which is due to API asymmetry between the client and server, automatically. However, by using client hints to derive a reliable initial value to populate the hooks with on the server, the developer can ensure a consistent hook state and avoid this mismatch.
In cases where this option is unavailable to the developer, I think that we should either:
useEffect
to calculate whether the prerequisite APIs are available now that the components are rendering on the client. This should avoid triggering a re-render in the event that the relevant APIs are unsupported in the user’s browser. If the APIs are available, then I agree with you that a re-render is necessary.E.g.
or
Use the
AdaptiveProvider
with ahydrate
flag – a pattern that I have seen work well for SSR before. The provider’s context could signal to the hooks when it is appropriate for them to re-run during the hydration phase.