Skip to content
Today's Tech Byte: Refs: from storing data to imperative API

Today's Tech Byte: Refs: from storing data to imperative API

Posted on:November 19, 2023

Ref as you may know is a way to access DOM nodes or React elements created in the render method. It’s a way to store values between renders.

Refs are created using React.createRef() and attached to React elements via the ref attribute. The ref attribute can be a callback function or a React.createRef() object.

This is a pretty common knowledge if you have been working with React for a while. But what is the difference between State and Refs? They both store data, right?

Well, the difference is that state is triggers re-renders, while refs don’t. Refs are not part of the React lifecycle, they are not affected by state changes or re-renders.

Ref is just a mutable object which makes its update synchronous. State on the other hand is asynchronous and , which means that when you update the state, you can’t be sure that the state has been updated yet.

The most common use case for refs is to access DOM nodes.

For example, if you want to focus on an input field when a button is clicked, you can do it like this:

const MyComponent = () => {
  const inputRef = React.createRef();

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleClick}>Focus</button>
    </div>
  );
};

If you have a custom component, you can use forwardRef to pass the ref to the underlying DOM node.

const FancyInput = React.forwardRef((props, ref) => (
  <input type="text" ref={ref} />
));

const MyComponent = () => {
  const inputRef = React.createRef();

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <FancyInput ref={inputRef} />
      <button onClick={handleClick}>Focus</button>
    </div>
  );
};

We use forwardRef instead of just passing the ref to the input as a prop because ref was a reserved word in Class components.

Now if we want to add some logic to the input, we can use the useImperativeHandle hook.


const FancyInput = React.forwardRef((props, ref) => {
  const inputRef = React.createRef();

  React.useImperativeHandle(ref, () => ({

    jump: () => {
      return //animate with css or something
    }
  }));

  return <input type="text" ref={inputRef} />;
});

This way, we can call the jump method from the parent component.

```javascript

const MyComponent = () => {
  const inputRef = React.createRef();

  const handleClick = () => {
    inputRef.current.jump();
  };

  return (
    <div>
      <FancyInput ref={inputRef} />
      <button onClick={handleClick}>Jump</button>
    </div>
  );
};

This is a pattern you nay have seen in UI libraries like Material UI. It’s a way to expose some methods to the parent component.

Thank you for reading.

If you have any suggestions or questions, feel free to reach out to me on Twitter or LinkedIn.

P.S. this post is inspired by the book, but it’s not a summary of it. I’m just sharing what I learned from it. If you want to learn more about React, I highly recommend you to read it.