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:
useRefvsuseState: When should you useuseRefinstead 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
useStateto manage thecountanduseRefto store the previous value ofcount. - The
prevCountRef.currentdoes 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
useRefto store values that need to persist across renders but don’t affect the UI. - Accessing DOM nodes or elements directly: Use
useReffor 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
useRefto 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
Inputcomponent is wrapped inforwardRefto allow therefto be passed down from theParent. - The
Parentcomponent holds the refinputRefand can directly interact with theInputfield (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:
useRefis 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
useRefto access form element values directly, without binding to state. forwardRefis 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.