Array of refs contains null values in useEffect cleanup function in React

Viewed 99

I'm trying to access a an array of refs during clean up (before the component unmounts). Like so:

const refs = useRef([]);

  useEffect(() => {
    let localRefs = null;

    if (refs.current) {
      localRefs = refs.current;
    }

    return () => {
      console.log("unmounting", localRefs);
    };
  }, []);

  return (
    <>
      <div
        ref={(el) => {
          refs.current[0] = el;
        }}
      ></div>
      <div
        ref={(el) => {
          refs.current[1] = el;
        }}
      ></div>
      <div
        ref={(el) => {
          refs.current[2] = el;
        }}
      ></div>
    </>
  );

In this case, the log outputs null. The refs appear to be created and stored properly on mount, but appear as an array with 3 null entries when logged during the cleanup.

1 Answers

You are not supposed to access an element reference in the clean up function as it literally runs after the component is removed from the DOM. Here is what React's official doc says:

When exactly does React clean up an effect? React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React also cleans up effects from the previous render before running the effects next time.

Normally there is no need for it, as the clean up is to stop side effects processes like an ongoing network request or to clear a setInterval, not to change DOM elements that are removed. It's designed to work this way:

export default function Component() {
  const refs = useRef([]);

  useEffect(() => {
    console.log("mount", refs.current); // prints [HTMLDivElement, HTMLDivElement, HTMLDivElement]

    return () => {
      console.log("unmount", refs.current); // prints [null, null, null]
    };
  }, []);

  return (
    <>
      <div ref={(el) => { refs.current[0] = el;}}>firstDiv</div>
      <div ref={(el) => { refs.current[1] = el;}}>secondDiv</div>
      <div ref={(el) => { refs.current[2] = el;}}>thirdDiv</div>
    </>
  );
}
Related