Unfortunately, useRef
is underrated. It is not among the most popular hooks, but it is beneficial. Knowing how and where to use it can achieve great results.
Let’s Start with the Basics
useRef
is a React Hook that lets you reference a value not needed for rendering.
React will remember the value you create through useRef
, whether you are making a JavaScript object referencing a node in the DOM or a simple value, and it will not be lost during re-renders.
What Does it Give Us?
Accessing DOM Elements:
- You can easily reach elements in the DOM. For example, you can get an input field’s value, focus on specific elements, get their height and width, scroll to a particular part of the screen, and more.
Storing Mutable Values:
- You can remember any data you need without re-rendering components. For example, if you need a counter or a timer, choose
useRef
overuseState
.
- You can remember any data you need without re-rendering components. For example, if you need a counter or a timer, choose
Examples
Here are some examples to illustrate the power of useRef
.
Example 1: Reference to a Number
import React, { useRef } from 'react'; const Counter = () => { const refCount = useRef(0); const refInputField = useRef(null); const onClick = () => { refCount.current = refCount.current + 1; refInputField.current.focus(); } return ( <> <button onClick={onClick}> Click me! </button> <input ref={refInputField} /> </> );
}; export default Counter;
In this example:
-
refCount
is a mutable reference to a number. -
refInputField
is a reference to an input element. - When the button is clicked, the counter increments, and the input field gains focus.
Example 2: Keeping Track of Previous Values
Another common use case for useRef
is keeping track of previous values.
import React, { useRef, useEffect, useState } from 'react'; const PreviousValue = () => { const [count, setCount] = useState(0); const prevCountRef = useRef(); useEffect(() => { prevCountRef.current = count; }, [count]); return ( <div> <h1>Current Count: {count}</h1> <h2>Previous Count: {prevCountRef.current}</h2> <button onClick={() => setCount(count + 1)}>Increment</button> </div> );
}; export default PreviousValue;
In this example:
-
prevCountRef
keeps track of the previous value ofcount
. - The
useEffect
hook updatesprevCountRef.current
whenevercount
changes. - This allows you to display both the current and previous counts without triggering unnecessary re-renders.
Advanced Tips and Tricks with useRef
1. Persisting Values Across Renders
useRef
can be used to persist values across renders without causing a re-render, unlike useState
. This is particularly useful for storing values that don’t directly impact the UI but need to be remembered.
Example: Tracking the render count of a component.
import React, { useRef, useEffect } from 'react'; const RenderCounter = () => { const renderCount = useRef(0); useEffect(() => { renderCount.current += 1; }); return ( <div> <p>This component has rendered {renderCount.current} times</p> </div> );
}; export default RenderCounter;
2. Integrating with Third-Party Libraries
useRef
is invaluable when working with third-party libraries that require direct manipulation of DOM elements, such as integrating with charting libraries, managing video players, or handling animations.
Example: Integrating a chart library.
import React, { useRef, useEffect } from 'react';
import Chart from 'chart.js/auto'; const ChartComponent = () => { const chartRef = useRef(null); useEffect(() => { const ctx = chartRef.current.getContext('2d'); new Chart(ctx, { type: 'line', data: { labels: ['January', 'February', 'March', 'April'], datasets: [{ label: 'Sales', data: [65, 59, 80, 81], }], }, }); }, []); return <canvas ref={chartRef}></canvas>;
}; export default ChartComponent;
3. Avoiding Unnecessary Re-Renders in Complex Applications
In complex applications where performance is critical, using useRef
to store mutable objects can help avoid unnecessary re-renders.
Example: Storing a mutable state object.
import React, { useRef } from 'react'; const MutableState = () => { const state = useRef({ name: 'John Doe', age: 30, }); const updateName = (newName) => { state.current.name = newName; console.log('Name updated:', state.current.name); }; return ( <div> <button onClick={() => updateName('Jane Doe')}> Update Name </button> </div> );
}; export default MutableState;
4. Avoiding Closure Issues
Using useRef
can help avoid closure issues by providing a stable reference to a value that persists across renders.
Example: Timer with useRef to avoid stale state.
import React, { useRef, useState, useEffect } from 'react'; const Timer = () => { const [count, setCount] = useState(0); const countRef = useRef(count); countRef.current = count; useEffect(() => { const intervalId = setInterval(() => { setCount(countRef.current + 1); }, 1000); return () => clearInterval(intervalId); }, []); return <div>Count: {count}</div>;
}; export default Timer;
Conclusion
Hooks are great, and you should use them. You can achieve a lot if you understand how React works and apply hooks correctly. useRef
is particularly powerful for:
- Accessing and manipulating DOM elements.
- Storing mutable values that do not trigger re-renders.
- Persisting values across renders.
- Integrating with third-party libraries.
- Avoiding unnecessary re-renders in complex applications.
- Mitigating closure issues.
By understanding and utilizing useRef
, you can write more efficient and effective React components. The true power of hooks lies in understanding their behavior and applying them judiciously.
Do you know, useState is not always the correct answer?
Discover more from Coursity
Subscribe to get the latest posts sent to your email.