DEV Community

Anton Korzunov
Anton Korzunov

Posted on • Edited on

18 5

The same useRef, but it will callback 🤙

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>
}


Enter fullscreen mode Exit fullscreen mode

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.

Hooks API Reference



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>
}


Enter fullscreen mode Exit fullscreen mode

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);


Enter fullscreen mode Exit fullscreen mode

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>
}


Enter fullscreen mode Exit fullscreen mode

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);


Enter fullscreen mode Exit fullscreen mode

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


Heroku

Simplify your DevOps and maximize your time.

Since 2007, Heroku has been the go-to platform for developers as it monitors uptime, performance, and infrastructure concerns, allowing you to focus on writing code.

Learn More

Top comments (1)

Collapse
 
joelstransky profile image
Joel Stransky

This completely solved my issue with connecting Greensock to React-Pixi components.

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay