For a long while we there were no refs - we had only ref , which was callback based. Something will set a ref by calling it.



class Example extends React . Component { state = { ref1 : null , } ref2 = null ; // updating ref1 would trigger update for this component setRef1 = ( ref ) => this . setState ( ref1 ); // updating ref2 would just set it setRef2 = ( ref ) => this . ref2 = ref ; render () { return < div ref = { ref1 } >< span ref = { ref2 } > 🤷‍♂️ < /span></ div > }

That was what we were doing for ages, until createRef comes to the game. React.createRef is more about ref2 way - current ref would just set to, well, ref.current .

Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render.

So - If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead. Ie the old way to _ref.





const Example = () => { const [ ref , setRef ] = useState ( null ); const onRefSet = useCallback ( ref => { setRef ( ref ); ref . current . focus (); // a side effect! }); // well, you can re return < div ref = { onRefSet } > 😎 < /div > }

But later you might try to combine ref-refs and callbacks-refs, and... well that's the road to 🔥hell🔥.

In addition - there is useImperativeHandle which partially could control ref propagation, but every time I was used to use it - it was just a 💩disaster💩.



function FancyInput ( props , ref ) { const inputRef = useRef ( null ); useImperativeHandle ( ref , () => ({ focus : () => { inputRef . current . focus (); // it just does not usually works :P } })); return < input ref = { inputRef } ... /> ; } FancyInput = forwardRef ( FancyInput );

LET'S FIX IT!

Introducing use-callback-ref - the same createRef and useRef , but with callback built in.



import { useCallbackRef } from ' use-callback-ref ' ; const Example = () => { const ref = useCallbackRef ( null , ref => ref && ref . focus ()); // that's all return < div ref = { ref } > 😎 < /div > }

It's literally the old good ref with an on-change callback, nothing more.

Why not to use callback-based ref? Well, it's much easier to handle one interface, which would be accessible thought all components that ref would be passed, well, thought - while with setRef only callback would be visible for transitional components. However, that could be a good from isolation point of view.

This simple approach could also help with useImperativeHandle case:



function FancyInput ( props , ref ) { const inputRef = useCallbackRef ( null , ( newValue ) => { // notice - this code is __isolated__, and you can move it off this component ref . current = { focus : () => newValue . focus () } // as long as you don't need to use callback-ref anymore - we could simply this case. }); return < input ref = { inputRef } ... /> ; } FancyInput = forwardRef ( FancyInput );

So - Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a useCallbackRef instead.

300b, and IE11 support

based on getters and setters, no Proxies involved

Try it now(codesandbox demo), and call me back later - https://github.com/theKashey/use-callback-ref