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
react-hooks eslint rules would even warn you about that. Notice how neither
ref.current as deps of
useEffect trigger it.
See this for yourself in the sandbox:
So what can we do?
We can rely on passing a regular function wrapped with
ref and react to the latest DOM node reference that it returns.
You can try it in the following sandbox:
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
Without it, if you cause a render from the
refcallback will be triggered again with
null, 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 is a function that is consistent between renders, it can also be used as a
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
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.
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
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