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
setRefonlycallbackwould 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
And there is the second part of this article

Top comments (1)
This completely solved my issue with connecting Greensock to React-Pixi components.