Understanding useRef() in React

What is useRef()?

First of all, let's compare useRef() with useState(). The main difference between them is, a change in useState() variable will always cause a re-render, whereas, a change in useRef() won't cause the re-render.

People use it for accessing the reference to the DOM element. But it does have more significance use than that.

import { useState, useEffect, useRef } from "react";
export default function App() {
  const [val, setVal] = useState("");
  const [renderVal, setRenderVal] = useState("");
  useEffect(() => {
    setRenderVal((prev) => prev + 1) 
  });
  return (
    <div className="App">
      <div ref={(el) => (this.componentRef = el)}>
        <input
          name="ans"
          onChange={(e) => setVal(e.target.value)}
        />

      </div>
    </div>
  );
}

Imagine what will be the output of this. useEffect does not contain any dependency array, and in the useEffect block we are trying to update the useState() variable and that will always cause a re-render, i.e it will run for an infinite amount of time and at the end, our app will crash. Let's solve this using useRef().

import "./styles.css";
import { useState, useEffect, useRef } from "react";
export default function App() {
  const [val, setVal] = useState("");
  const render = useRef(0);
  useEffect(() => {
    render.current = render.current + 1;
  });
  return (
    <div className="App">
        <input
          name="ans"
          onChange={(e) => setVal(e.target.value)}
        />
    We renders {render.current} times.
    </div>
  );
}

Whenever you use useRef. It will create an object internally. For like, here we have created a render using useRef. Internally, it will look something like this;

render: {
    current: 0
}

It does have one property called current which will help us in the persistence of the data.

If we want to access the value from the useRef() current property of the useRef variable helps.

Accessing DOM reference using useRef.

In this example, we'll try access the <input /> using the useRef

import "./styles.css";
import { useState, useEffect, useRef } from "react";

export default function App() {
  const [val, setVal] = useState("");
  const render = useRef();

  function handleFocus() {
    render.current.focus();
  }
  return (
    <div className="App">
      <input ref={render} name="ans" onChange={(e) => setVal(e.target.value)}/>
      <button onClick={handleFocus}>Focus the Input</button>
      <br />
    </div>
  );
}

Each DOM element has ref property which can bind the useRef() value. Here we are binding render with it and we can access all the properties of that element.

For example, here we created one button and that button helps us in focusing the element. We must remember if we want to access the value of useRef() - that must be done by accessing the current property of the useRef() variable and after that we can use any method DOM might contain.

Here, let's see how a useRef() variable can persist the data.

import "./styles.css";
import { useState, useEffect, useRef } from "react";
export default function App() {
  const [val, setVal] = useState("");
  const render = useRef();
  useEffect(() => {
    render.current = val;
  }, [val]);
  return (
    <div className="App">
        <input
          ref={render}
          name="ans"
          onChange={(e) => setVal(e.target.value)}
        />
        <br />
        My name is {val} and it used to be {render.current}
      </div>

  );
}

Simply, in useEffect() we are storing the previous value of val in render variable.

What happens here is, we are making changes in the val which is useState() variable and changes in useState will always cause re-render.

So, when we are re-rendering we are storing the current value in useRef variable.

We can use this useRef for printing our screen as well, I am not diving into details for now. But the entire demo is available here:

Check useRef() demo

I hope this helps. Please drop comments if you're facing any problems.