Instead of an object returned by useRef, you can use a 'ref callback', which is a function passed to the ref attribute.
As of React 19 this also now has a [[20250219104717-ref-cleanup]] function.
<div ref={(node) => {
console.log('Attached', node);
return () => {
console.log('Clean up', node)
}
}}>
This is often useful to avoid calling things in a [[20220815044638-useeffect]], as they don't trigger when a ref changes.
When the
<div>DOM node is added to the screen, React will call yourrefcallback with the DOMnodeas the argument. When that<div>DOM node is removed, React will call your the cleanup function returned from the callback.
Note that this is also called when you pass a different ref callback.
React will also call your
refcallback whenever you pass a differentrefcallback. In the above example,(node) => { ... }is a different function on every render. When your component re-renders, the previous function will be called withnullas the argument, and the next function will be called with the DOM node.
Strict Mode will call an extra pass on these functions to test for resiliency. So in most cases you'll see four calls for this when developing.
You can use ref callbacks to manage a list of refs [[20250519010717-ref-lists]]
https://react.dev/reference/react-dom/components/common#ref-callback
#todo - how to share a ref?
const combineRefs = el => { internalRef.current = el if (typeof externalRef === 'function') { externalRef(el) } else { externalRef.current = el } }
Keep in mind the caveat around inline functions below
More complicated versions exist to handle using .current, multiple refs, Typescript, etc. It starts to get a little confusing and that's why NPM packages exist for it.