Published on 15.12.2020
In React, managing references to DOM elements and component instances has always been an important part of building complex user interfaces. While React’s state system is ideal for most interactions, there are scenarios where you need to persist values or access DOM nodes directly without causing re-renders. This is where Refs come into play.
In this post, we will explore the following:
useRef
vsuseState
: When should you useuseRef
instead ofuseState
?- Managing uncontrolled form inputs with
useRef
. - Passing refs to child components using
forwardRef
.
By the end of this article, you’ll have a solid understanding of React Refs, and how to use them effectively in your components.
What is a React Ref?
A Ref (short for reference) in React is a way to access the DOM or instance of a component directly. It allows you to bypass React’s state-driven re-render cycle and interact with elements or values without causing a re-render.
Ref usage is common for tasks such as:
- Managing focus on an input element.
- Measuring the size or position of an element on the screen.
- Triggering animations or manual changes to DOM elements.
useRef vs. useState: When to Use Each
In React, we primarily use useState
to hold values that affect the render cycle of the component, causing the component to re-render when the state changes. However, there are cases where you may want to store a value without triggering a re-render.
useRef
is a hook that allows you to store a value that does not trigger re-renders. This makes it ideal for cases where the value does not need to be rendered but must persist across renders.
useRef Example: Managing Mutable Values
Consider an example where you need to track the previous state of a value, but you don’t want to trigger a re-render every time it changes:
import React, { useState, useEffect, useRef } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count; // store the current count
}, [count]); // update the ref when count changes
return (
<div>
<p>Current count: {count}</p>
<p>Previous count: {prevCountRef.current}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default Counter;
In this example:
- We use
useState
to manage thecount
anduseRef
to store the previous value ofcount
. - The
prevCountRef.current
does not cause a re-render when it changes, unlike state stored inuseState
.
When to use useRef
instead of useState
:
- Tracking values that don’t need to trigger a re-render: Use
useRef
to store values that need to persist across renders but don’t affect the UI. - Accessing DOM nodes or elements directly: Use
useRef
for interacting with DOM elements, like focusing an input field.
On the other hand, use useState
when you need the value to affect the component’s render and cause re-renders when it changes.
Managing Uncontrolled Form Inputs with useRef
In React, forms can be either controlled or uncontrolled. Controlled components manage form input values via React state, while uncontrolled components manage form inputs using the DOM itself (via refs).
For uncontrolled form inputs, useRef
is ideal, as it allows you to interact with the form elements without explicitly binding the values to the component state.
Example: Using useRef
for Uncontrolled Inputs
import React, { useRef } from 'react';
function UncontrolledForm() {
const nameInputRef = useRef();
const handleSubmit = (event) => {
event.preventDefault();
// Access the value of the input using the ref
alert(`Name: ${nameInputRef.current.value}`);
};
return (
<form onSubmit={handleSubmit}>
<input ref={nameInputRef} type="text" placeholder="Enter your name" />
<button type="submit">Submit</button>
</form>
);
}
export default UncontrolledForm;
In this example:
- We use
useRef
to reference the input element (nameInputRef
). - The form’s value is accessed directly from the DOM using
nameInputRef.current.value
, which is uncontrolled as opposed to a controlled input that would rely on state.
Uncontrolled components are useful when you need to integrate with third-party libraries or handle large forms without needing to manage each input’s state.
Passing Refs to Child Components with forwardRef
In certain scenarios, you may need to pass a ref from a parent component down to a child component. React provides the forwardRef
API to allow a parent component to access the ref of a child component.
Using forwardRef
to Pass Refs
The forwardRef
function is used to wrap a child component and allow it to receive the ref from its parent.
import React, { useRef } from 'react';
// Child component with forwardRef
const Input = React.forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});
function Parent() {
const inputRef = useRef();
const handleFocus = () => {
// Focus the input field directly from the parent
inputRef.current.focus();
};
return (
<div>
<Input ref={inputRef} placeholder="Focus me!" />
<button onClick={handleFocus}>Focus the input</button>
</div>
);
}
export default Parent;
In this example:
- The
Input
component is wrapped inforwardRef
to allow theref
to be passed down from theParent
. - The
Parent
component holds the refinputRef
and can directly interact with theInput
field (e.g., focusing the input).
When to use forwardRef
:
- When you need to pass a ref to a child component that is a functional component.
- When you need to interact with a DOM element inside a child component.
Note that forwardRef
is only necessary when you need to expose a ref to a functional component. Class components automatically forward refs via the ref
attribute.
Conclusion
Understanding React Refs and when to use them is essential for managing DOM elements and mutable state without unnecessary re-renders. Here’s a summary of the key points covered in this post:
useRef
is perfect for managing mutable values that don’t need to trigger re-renders. It’s useful for persisting data across renders without affecting the UI.- Uncontrolled form inputs can be managed using
useRef
to access form element values directly, without binding to state. forwardRef
is the solution when you need to pass a ref from a parent to a child component, allowing direct access to child DOM elements or component instances.
By using React’s refs appropriately, you can create more efficient, performant components while maintaining cleaner, more predictable state management. Understanding how and when to use these tools will help you build more robust and scalable applications in React.