Skip to content

Bug: Hydration errors if useEffect+setState that modifies rendered html is not done directly in the final component. #33636

Closed
@alicerocheman

Description

@alicerocheman

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

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Status: UnconfirmedA potential issue that we haven't yet confirmed as a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions