State Management in React: Exploring useRef and useState
Harnessing the Power of useRef and useState in React: Exploring Differences, Similarities, and Combined Use
React is a powerful JavaScript library for building user interfaces, and it provides several tools and hooks for managing state and interacting with the DOM.
Two commonly used hooks in React are useRef
and useState
.
While both are essential for various use cases, they have different purposes and behaviours.
In this article, we'll explore the differences and similarities between useRef
and useState
, along with code samples to illustrate their usage.
useRef
Hook
The useRef
hook in React allows you to reference a value that is not needed for rendering. It is typically used for:
Referencing a DOM element.
Storing information between re-renders.
Managing data that does not impact the visual output of a component.
Basic Usage of useRef
import React, { useRef } from 'react';
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...
return (
// ...
);
}
In the code above, we create two useRef
instances: intervalRef
and inputRef
.
intervalRef
is used to store information (an interval ID) between renders.inputRef
is used to reference a DOM element (in this case, an<input>
).
Key Points about useRef
Changing a
useRef
Does Not Trigger Re-render: Unlike state variables, changing a valueuseRef
does not cause a component to re-render.
This makes it suitable for storing information that should not affect the component's visual output.Do Not Write or Read
ref.current
During Rendering: Writing to or reading fromref.current
during the rendering phase can lead to unexpected behaviour.
It's best to interact withref.current
in event handlers or effects.
Example: Click Counter Using useRef
In the following example, we use a useRef
to keep track of how many times a button is clicked:
import React, { useRef } from 'react';
function Counter() {
let ref = useRef(0);
function handleClick() {
ref.current = ref.current + 1;
alert('You clicked ' + ref.current + ' times!');
}
return (
<button onClick={handleClick}>
Click me!
</button>
);
}
This example demonstrates how useRef
can be used to store information between re-renders without triggering re-rendering.
useState
Hook
The useState
hook in React is used to manage component-level state.
It is typically employed for:
Managing data that affects the visual output of a component.
Triggering re-renders when state changes.
Basic Usage of useState
import React, { useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
// ...
return (
// ...
);
}
In this example, we use the useState
hook to create a state variable count
and a function setCount
to update its value.
Changing count
triggers a re-render of the component.
Key Points about useState
State Variables Trigger Re-renders: Changing a state variable, such as
count
in the above example, triggers a re-render of the component.
This is because state variables affect the visual output of the component.Best for Displayed Data:
useState
is best suited for managing data that should be displayed on the screen, as it automatically updates the UI when the state changes.
Example: Click Counter Using useState
In this example, we use the useState
hook to create a simple click counter:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<button onClick={handleClick}>
Click me!
</button>
<p>You clicked {count} times!</p>
</div>
);
}
Here, we use useState
to manage the count, and each click on the button updates the displayed count.
Differences and Similarities
Differences
Purpose:
useRef
is primarily used for referencing values that do not affect rendering, such as DOM elements or non-displayed data.useState
, on the other hand, is used for managing state variables that do affect rendering.Re-rendering: Changing a
useRef
value does not trigger a re-render while changing auseState
value does.
Similarities
Both are Hooks: Both
useRef
anduseState
are hooks provided by React and can be used in functional components.Immutable Updates: When updating values, it's a good practice to create a new reference or state value.
ForuseRef
, this is achieved by changing thecurrent
property.
ForuseState
, you use the updater function or the spread operator to create a new state object.Initialization: Both hooks allow you to provide an initial value when creating the reference or state variable.
Local to Component: Values stored in
useRef
or state variables are local to the component that defines them, making them suitable for component-specific data.
Choosing Between useRef
and useState
When deciding whether to use useRef
or useState
, consider the following guidelines:
Use
useRef
for non-render-impacting data or DOM manipulation.Use
useState
for managing the state that impacts the visual output of the component.
In practice, these hooks can often complement each other within the same component, with useState
handling displayed data and useRef
managing auxiliary information or DOM elements.
Using useRef
and useState
Together
In React, there are scenarios where you need to combine the power of both useRef
and useState
to create efficient and dynamic components.
We can explore this combination with an example of a stopwatch.
Example: Stopwatch Using useRef
and useState
Suppose you want to build a stopwatch that keeps track of elapsed time while allowing users to start and stop it.
Here, you can use useState
to manage the running state and the displayed time, while useRef
comes in handy for storing the interval ID, ensuring it doesn't trigger re-renders.
import React, { useState, useRef } from 'react';
function Stopwatch() {
const [isRunning, setIsRunning] = useState(false);
const [time, setTime] = useState(0);
const intervalRef = useRef(null);
const startStop = () => {
if (isRunning) {
clearInterval(intervalRef.current);
} else {
intervalRef.current = setInterval(() => {
setTime((prevTime) => prevTime + 1);
}, 1000);
}
setIsRunning(!isRunning);
};
const reset = () => {
clearInterval(intervalRef.current);
intervalRef.current = null;
setTime(0);
setIsRunning(false);
};
return (
<div>
<h1>Stopwatch</h1>
<p>Time: {time} seconds</p>
<button onClick={startStop}>{isRunning ? 'Stop' : 'Start'}</button>
<button onClick={reset}>Reset</button>
</div>
);
}
In this example, useState
is used to manage the running state (isRunning
) and the displayed time (time
). On the other hand, useRef
is used to store the interval ID (intervalRef
) for managing the timer logic.
This combination allows us to create a dynamic stopwatch that efficiently updates the time without causing unnecessary re-renders.
When to Use the Combined Approach
Use
useState
for managing state variables that affect the visual output of your component.Use
useRef
for referencing values that are not needed for rendering or for storing non-render-impacting data.Combine both hooks when you need to maintain non-render-impacting data and state that affects the visual output within the same component.
The combined approach allows you to create versatile React components, such as the stopwatch, that efficiently manage different aspects of their behaviour while adhering to best practices in React development.
By leveraging both useRef
and useState
, you can build dynamic and interactive components that are both performant and maintainable.
In conclusion, both useRef
and useState
are essential tools in a React developer's toolkit. Understanding their differences and similarities and knowing when to use each or both can help you build more efficient and maintainable React components.