How to Update useCallback Hook in React
This article explains how to properly update and manage the
useCallback hook in React. You will learn how the
dependency array triggers function updates, how to use functional state
updates to prevent unnecessary recreation of the callback, and how to
access the latest values using refs.
Understanding the Dependency Array
To update a useCallback hook, you must correctly
configure its dependency array (the second argument passed to the hook).
React memoizes the function definition and will only update (re-create)
the function when one of the dependencies listed in the array
changes.
import { useCallback, useState } from 'react';
function UserComponent() {
const [userId, setUserId] = useState(1);
// This callback updates only when userId changes
const fetchUserData = useCallback(() => {
fetch(`https://api.example.com/users/${userId}`)
.then(res => res.json())
.then(data => console.log(data));
}, [userId]);
}If you omit a dependency that is used inside the function, the function will suffer from “stale closures,” meaning it will continue to reference the old values from when the function was first created.
Updating State Without Triggering Callback Updates
A common mistake is adding a state variable to the dependency array
just to update that state. This causes the useCallback to
recreate on every state change, defeating the purpose of
memoization.
You can update state and avoid recreating the callback by using a functional state update:
import { useState, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// Bad: Recreates the function on every count change
const incrementBad = useCallback(() => {
setCount(count + 1);
}, [count]);
// Good: Never recreates because the dependency array is empty
const incrementGood = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []);
}Using Refs for Frequently Changing Values
If you need to access a rapidly changing value inside your callback
but do not want the callback to update and trigger rerenders of child
components, you can store the value in a useRef.
import { useState, useCallback, useRef, useEffect } from 'react';
function FormComponent({ onSubmit }) {
const [text, setText] = useState("");
const textRef = useRef(text);
// Keep the ref updated with the latest state
useEffect(() => {
textRef.current = text;
}, [text]);
// Callback does not update when text changes, but always has the latest value
const handleSubmit = useCallback(() => {
onSubmit(textRef.current);
}, [onSubmit]);
}