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 yourref
callback with the DOMnode
as 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
ref
callback whenever you pass a differentref
callback. In the above example,(node) => { ... }
is a different function on every render. When your component re-renders, the previous function will be called withnull
as 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.