Description
Hydration errors if useEffect+setState that modifies rendered html is not done directly in the final component.
=> hydration error if useEffect+setState is done inside a hook
=> hydration error if useEffect+setState is done in the parent component
=> no error if useEffect+setState is done in the final component that renders the html
React version: 19.1.0
in a react-router (7.6.2) framework app with React Compiler (19.1.0-rc.2)
Steps To Reproduce
- create a working component, with an element that will scale up only after hydration:
const MyComponent = () => {
const [scaleOnClientClassName, setScaleOnClientClassName] = useState('scale-y-0');
useEffect(() => {
setScaleOnClientClassName('scale-y-100');
}, []);
return <button className={`some classes ${scaleOnClientClassName}`}>Hello</button>·
}
=> this works fine, no hydration error
2. now put the scaleOnClient logic in a hook:
const useScaleOnClientClassName = () => {
const [scaleOnClientClassName, setScaleOnClientClassName] = useState('scale-y-0');
useEffect(() => {
setScaleOnClientClassName('scale-y-100');
}, []);
return scaleOnClientClassName;
}
const MyComponent = () => {
const scaleOnClientClassName = useScaleOnClientClassName();
return <button className={`some classes ${scaleOnClientClassName}`}>Hello</button>·
}
=> hydration error on the className line
3. now go back to step one, but instead of rendering the directly, render a ChildComponent that will render the button:
const MyChildComponent = (className) => {
return <button className={`some classes ${className}`}>Hello</button>·
}
const MyParentComponent = () => {
const [scaleOnClientClassName, setScaleOnClientClassName] = useState('scale-y-0');
useEffect(() => {
setScaleOnClientClassName('scale-y-100');
}, []);
return <MyChildComponent className={`some classes ${scaleOnClientClassName}`} />·
}
=> hydration error on the className line
All of these examples should work without hydration error.
I will try to make a reproduction repo if i find the time later today.
The current behavior
=> hydration error if useEffect+setState is done inside a hook
=> hydration error if useEffect+setState is done in the parent component
=> no error if useEffect+setState is done in the final component that renders the html
The expected behavior
=> no error if useEffect+setState is done inside a hook
=> no error if useEffect+setState is done in the parent component
=> no error if useEffect+setState is done in the final component that renders the html