useCallback Might Be What You Meant By useRef & useEffect
Sometimes we respond to React element mounts. The first instinct is to useRef & useEffect. But It’s wrong. What you really need is often useCallback.
If you want to respond to a React element’s mounting on the DOM, you may be tempted to use useRef to get a reference to it and useEffect to respond to its mounts and unmounts. But it won’t work.
This is because there is no callback or re-render when a component is (un)mounted and attached to ref.current using useRef.
The react-hooks eslint rules would even warn you about that. Notice how neither ref nor ref.current as deps of useEffect trigger it.
See this for yourself in the sandbox:
So what can we do?
useCallback
(link to the official React docs regarding this)
We can rely on passing a regular function wrapped with useCallback to ref and react to the latest DOM node reference that it returns.
You can try it in the following sandbox:
Caveat: the
reffunction is guaranteed to be called on mounts and unmounts of elements, even on the first mount, and even if the unmount is as a result of the parent element unmounting.Caveat2: Make sure to wrap
refcallbacks withuseCallback.Without it, if you cause a render from the
refcallback, therefcallback will be triggered again withnull, potentially leading to an endless loop, because of React internals.To play with this, try removing
useCallbackfrom the sandbox above and see this other sandbox.
This pattern can be used in several more ways:
useState
Since useState is a function that is consistent between renders, it can also be used as a ref.
In this case, the whole node will be saved in its own state.
As a state, when it changes, it would trigger a re-render and the state can be safely used in the render’s results and as a useEffect dependency:
useStateRef
Accessing the DOM is expensive, so we want to do this as little as possible.
If you don’t need the whole node like in the previous hook, you better save only part of it on a state:
As you can see we access the DOM only when the element to which we pass ref is mounted and only save clientHeight in this stage.
useRefWithCallback
But sometimes, for the sake of performance, you can do without triggering re-renders on mounts and unmounts of the element that you use it’s ref.
The following hook doesn't save the node on the state. Instead of using state, it responds to mounts and unmounts directly, so it doesn’t trigger any re-renders.
In the end, if you understand the principle behind using useCallback as an element ref you might come up with your own ideas tailored for your specific needs.
Happy Hooking :D

