Ref Hooks

Refs let a component hold some information that isnt used for rendering and they are useful when you need to work with non-React systems.


  • useRef You can hold any value in it, but most often its used to hold DOM node.
  • useImperativeHandle lets you customize the ref exposed by your component.

useRef

Used for saving DOM node and timeout ID.

Caveats

  • The ref.current property can mutate. Unlike state, it is mutable. However, if it holds an object that is used for rendering, then you shouldn’t mutate that object.

  • Changing a ref does not trigger a re-render. React is not aware of when you change it because a ref is a plain JavaScript object.

  • Do not write or read ref.current during rendering, except for initialization. This makes your component’s behavior unpredictable. (example: don'ts)

Example: Don'ts

function Main() {
  useEffect(() => {
    // You can read or write refs in effects
    ref.current = "something";
  });

  function handleClick() {
    // You can read or write refs in event handlers
    doSomething(myOtherRef.current);
  }
}
function Main() {
  // Don't write a ref during rendering
  ref.current = "something";

  // Don't read a ref during rendering
  return <h1>{myOtherRef.current}</h1>;
}

Example: basic

const intervalRef = useRef(null);

function handleStart() {
  setStartTime(Date.now());
  setNow(Date.now());

  clearInterval(intervalRef.current);
  intervalRef.current = setInterval(() => {
    setNow(Date.now());
  }, 10);
}

useImperativeHandle

If you don’t want to expose the entire <input> DOM node, but you want to expose two of its methods: focus and scrollIntoView. To do this, keep the real browser DOM in a separate ref. Then use useImperativeHandle to expose a handle with only the methods that you want the parent component to call

Do not overuse refs. You should only use refs for imperative behaviors that you can’t express as props: for example, scrolling to a node, focusing a node, triggering an animation, selecting text, and so on.

If you can express something as a prop, you should not use a ref. For example, instead of exposing an imperative handle like { open, close } from a Modal component, it is better to take isOpen as a prop like <Modal isOpen={isOpen} />. Effects can help you expose imperative behaviors via props.

Parameters

  • The ref you received as the second argument from the forwardRef render function.

  • A function that takes no arguments and returns the ref handle you want to expose.

  • The list of all reactive values referenced inside of the function. If a re-render resulted in a change to some dependency, or if you omitted this argument, this function will re-execute, and the newly created handle will be assigned to the ref.

Example: basic

import { forwardRef, useImperativeHandle, useRef } from "react";

const MyInput = forwardRef(function MyInput(props, ref) {
  const inputRef = useRef(null);

  useImperativeHandle(
    ref,
    () => {
      return {
        focus() {
          inputRef.current.focus();
        },
        scrollIntoView() {
          inputRef.current.scrollIntoView();
        },
      };
    },
    []
  );

  return <input {...props} ref={inputRef} />;
});

export default MyInput;
import { useRef } from "react";

import MyInput from "./MyInput.js";

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
    // This won't work because the DOM node isn't exposed:
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput placeholder="Enter your name" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}