useRef — React Hook

AkashSDas
3 min readMar 28, 2024

--

useRef is used to create a mutable reference to a DOM element or to persist values across renders without causing a re-render when the value changes (values that aren’t need for rendering).

Following is the signuature of useRef hook:

const ref = useRef(initialValue);

Here, the initialValue is the initial value of ref’s current object. It can be a value of any type. This argument is ignored after the initial render. useRef returns an object with single property i.e. current.

useRef returns the same object across renders i.e. React saves the initial ref value once and ignores it on the next renders.

If we pass the ref object to React as a ref attribute to a JSX node, React will set its current property as the DOM node.

After React creates the DOM node and puts it on the screen, React will set the current property of our ref object to that DOM node. React will set the current property back to null when the node is removed from the screen.

Following is an usage of useRef hook:

import { useEffect, useRef, useState } from "react";

export default function App(): JSX.Element {
const countRef = useRef<number>(0);
const [count, setCount] = useState<number>(0);

useEffect(
function () {
countRef.current += 1;
console.log("[useEffect] ", countRef.current);
},
[count]
);

countRef.current += 1;
console.log("[render] ", countRef.current);

return (
<section>
<h2>Current count ref: {countRef.current}</h2>

<button onClick={handleClick}>Count: {count}</button>
</section>
);

function handleClick(): void {
setCount(function (prev) {
console.log("[setCount]");
return prev + 1;
});
}
}

// Output in the initial render:
// [render] 1
// [render] 1
// [useEffect] 2
// [useEffect] 3

🚨 In Strict Mode (development), React will call our component function twice in order to help you find accidental impurities. Each ref object will be created twice, but one of the versions will be discarded. If our component function is pure (as it should be), then this shouldn’t affect the behavior.

By using ref we get following benefits:

  • We can store information between re-renders (unlike regular variables, which reset on every render).
  • Changing it doesn’t trigger a re-render (unlike state variables, which trigger a re-render).
  • The information is local to each copy of our component (unlike the variables outside, which are shared).

Another example of useRef where we want to focus on the input on button click. Also, here we’ll use forwardRef to pass ref to our components.

import { forwardRef, useRef } from "react";

export default function App(): JSX.Element {
const ref = useRef<HTMLInputElement | null>(null);

return (
<div>
<button onClick={focusOnInput}>Focus</button>
<CustomTextInput ref={ref} />
</div>
);

// A pattern to ensure that ref.current is not null
function getInputRef(): HTMLInputElement {
if (ref.current === null) {
throw new Error("Input ref is null");
}

return ref.current;
}

function focusOnInput(): void {
const currentRef = getInputRef();
currentRef.focus();
currentRef.placeholder = "Hello";

// ref.current?.focus();
// ref.current!.placeholder = "Hello";
}
}

const CustomTextInput = forwardRef(function CustomTextInput(
_props: Record<string, unknown>,
ref: React.Ref<HTMLInputElement>
) {
return (
<div>
<input type="text" ref={ref} />
</div>
);
});

We can mutate the ref.current property. Unlike state, it is mutable. However, if it holds an object that is used for rendering (for example, a piece of our state), then we shouldn’t mutate that object.

Also, we shouldn’t read/write ref.current during rendering, (except for initialization), as this makes our component’s behavior unpredictable. This is because React expects that the body of our component should behaves like a pure function.

  • If the inputs (props, state, and context) are the same, it should return exactly the same JSX.
  • Calling it in a different order or with different arguments shouldn’t affect the results of other calls. Reading/writing a ref during rendering breaks these expectations. If we’ve to read/write something during rendering, then we should use state instead of ref. When we break these rules, our component might still work, but most of the newer features that are being added to React will rely on these expectations.

--

--