A Guide to Managing References Without Re-renders
In React, one of the most powerful hooks available is useRef
. This hook allows us to persist values between renders without triggering a re-render, making it an essential tool for managing certain types of data that don't belong in the component's state.
In this blog, we'll explore what refs
are, how to use useRef
, and provide an example to illustrate its behavior.
What is a Ref?
A ref stands for "reference." Essentially, it's like a box into which you can put any data that you want to preserve between renders.
When we use the useRef
hook, React gives us an object with a mutable current
property, which we can use to store any data we want. Unlike state, updating this current
property will not trigger a re-render.
Key Characteristics of useRef
Persistent Data:
useRef
is useful for storing data that you want to keep around across renders, but which doesn't directly affect the UI. For example, tracking the previous value of a prop or managing a timer ID.Not Rendered in JSX:
useRef
is typically used for values that are not part of the rendered output, meaning you generally use it within event handlers or side effects (likeuseEffect
). If you want to display or use values within your JSX, it's better to use state.No Re-renders: A crucial difference between refs and state is that updating a ref's
current
value does not cause a re-render. This makesuseRef
ideal for managing things like DOM elements or other mutable values that change over time but shouldn’t force a component update.
Best Practices
- Avoid Reading/Writing
current
in Render Logic: Unlike state, refs should not be read or written during the render phase of the component. Doing so can lead to unexpected behavior or hard-to-debug issues, as the ref's value can change without causing the component to update.
Code Example: Using useRef
to Manage a DOM Element
Here's an example of how to use useRef
to interact with a DOM element without triggering re-renders.
import React, { useRef } from "react";
function TextInputFocus() {
// Create a ref to store the text input DOM element
const inputRef = useRef(null);
const handleFocus = () => {
// Access the input's DOM node and focus on it
if (inputRef.current) {
inputRef.current.focus();
}
};
return (
<div>
<input ref={inputRef} type="text" placeholder="Click the button to focus" />
<button onClick={handleFocus}>Focus the Input</button>
</div>
);
}
export default TextInputFocus;
Explanation:
We create a ref using
useRef(null)
, and assign it to the input element withref={inputRef}
.The
handleFocus
function, triggered by the button click, uses the ref to access the input element's DOM node and focus on it. This operation does not cause a re-render because we're only modifying the DOM element, not the component state.
When Should You Use useRef
?
Accessing DOM elements: As shown in the example,
useRef
is perfect for interacting with DOM elements directly.Storing mutable data: If you need to store some data that changes over time but doesn’t affect the UI (like a timer ID or previous prop value),
useRef
is the right choice.Preserving values between renders: Since
useRef
persists data across renders without causing updates, it's a great tool for maintaining non-UI-related values.
Important Notes
Refs don’t cause re-renders: Unlike state, updating a ref’s
current
property won’t re-render the component. This makes refs useful for avoiding unnecessary UI updates when only non-rendering logic is affected.State for UI updates, refs for side effects: When working with values that affect the UI, use state. For everything else—like tracking timers, storing previous values, or interacting with DOM nodes—use refs.
By understanding and properly using useRef
, you can avoid unnecessary re-renders and handle mutable values efficiently in your React components.
Feel free to test out the example and see how you can apply useRef
in your own projects! Happy coding! 😊