How to use the React useRef hook

January 04, 2022React

3 min read

The React useRef hook accepts one argument as the initial value and returns a reference.

  • The reference that is returned is an object with a special property, called current.
  • ref.current accesses the reference value.
  • ref.current = newValue updates the reference value.

We can use useRef to store any value on its .current property.

References versus state

  • Persistence: The value of a reference is persisted (does not change) between component re-renders.
  • No re-renders triggered: Updating a reference doesn’t trigger a component re-render.

useRef to access DOM elements

The useRef hook is useful for accessing DOM elements. One use case for this is to add focus on an input field when a component mounts.

  • Step 1: define the reference: const inputRef = useRef(null)
  • Step 2: assign the reference to the element: <input ref={inputRef} />

Once a component is mounted, inputRef.current will point to the DOM element. We can then manipulate this DOM element via the inputRef.current reference.

UserForm.jsx
export const UserForm = () => {
  const inputRef = useRef(null);

  // inputRef.current is null upon initial render
  // There's no DOM structure created yet
  console.log("inputRef is", inputRef.current);

  // access useRef references inside useEffect
  useEffect(() => {
    inputRef?.current?.focus();
  }, []);

  return (
    <div>
      <label htmlFor="username">Username</label>
      <input id="username" ref={inputRef} type="text" />
    </div>
  );
};

It is important to note that useRef references must be referenced and used only once the DOM structure has been mounted. The useEffect hook allows us to run code once the DOM structure has been created.

The first parameter passed to useEffect is a function containing the code we want to run after the DOM is mounted for this component. The second parameter is an empty array [], which tells useEffect to run only once, after the component first renders. Not including the empty array would have useEffect run after every component render, that is, the initial render and every re-render. This is not what we need for this example, and it is also more expensive performance-wise.

Managing state with useRef

The useRef hook can be used as a stateful React hook. We can use the useRef hook to manage component state without having to trigger a component re-render like useState does.

Counter.jsx
const Counter = () => {
  const countRef = useRef(0);

  const handleClick = () => {
    countRef.current++;
    console.log(countRef.current);
  };

  return (
    <>
      <h1>Count: {countRef.current}</h1>
      <button onClick={handleClick}>Click</button>
    </>
  );
};

When clicking the button, the current property of countRef is incremented by 1 and the result of this is correctly logged to the screen. The value of countRef.current will be persisted no matter how many times this component re-renders due to prop or state changes.

Even if the current property of countRef has its value updated on every click, the component will not show the updated value in the HTML output of Count: {countRef.current}. It will continue displaying the initial value of 0. This is because useRef does not trigger the re-rendering of a component. If a component does not re-render, it will not be able to display updates to values that occured after its initial render to the screen.

Conclusion

Let's review some important useRef lessons that we learned in this article.

  • useRef references can access DOM elements.
  • The value of a a useRef reference is persisted across component re-renderings.
  • Updating a reference with ref.current = newValue, is not like updating state with useState, it does not trigger a component re-render.