Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React hook useLayoutEffect not updating ref parameter on initial render

Tags:

reactjs

I'm trying to run a function to check the contrast of any DOM node against its background, passing a reference to the node itself as an argument. But on the initial render, the ref value stays null, I don't understand why.

I understand using useLayoutEffect instead of useEffect should fire synchronously. Also, maybe the issue is with the ref itself, but shouldn't the useRef do the trick?

Here's a sandbox with the full code.

This is the component part:

function App() {
  const ref = useRef(null);
  const contrast = useContrast(ref);

  return (
    <div className="App">
      <h1 ref={ref}>Contrast: {contrast}</h1>
    </div>
  );
}

And the custom hook part:

export default function useContrast(ref) {
  const [contrast, setContrast] = useState("");

  if (!!ref.current) {
    useLayoutEffect(() => {
      const contrast = contrastValidation(
        getForegroundColor(ref.current),
        getBackgroundColor(ref.current)
      );

      setContrast(contrast);
    });
    return ref.current;
  }

  return contrast;
}

It should display some contrast value here: <h1 ref={ref}>Contrast: {contrast}</h1>, but all I get stays null (as it is defined by default with the useRef).

like image 536
TommyEm Avatar asked Mar 25 '19 14:03

TommyEm


1 Answers

ref.current will be set inside the function given to useLayoutEffect, but it will not be set before it, so you can't do if (!!ref.current) { ... } to conditionally use the hook or not. You should not put hooks inside conditional logic at all.

This will work as you expect since state updates that occur inside the function given to useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

export default function useContrast(ref) {
  const [contrast, setContrast] = useState("");

  useLayoutEffect(() => {
    const contrast = contrastValidation(
      getForegroundColor(ref.current),
      getBackgroundColor(ref.current)
    );

    setContrast(contrast);
  });

  return contrast;
}
like image 79
Tholle Avatar answered Dec 01 '22 19:12

Tholle