The useCallback hook helps you keep a function the same between renders unless its inputs change. This saves work and avoids mistakes when React checks for changes.
useCallback hook in React
const memoizedFunction = useCallback(() => { // function code here }, [dependencies]);
The first argument is the function you want to keep the same.
The second argument is an array of values that, when changed, will recreate the function.
count changes.const increment = useCallback(() => { setCount(count + 1); }, [count]);
const sayHello = useCallback(() => { console.log('Hello!'); }, []);
This component shows a count and a button. The increment function is created once and reused, so React won't recreate it on every render. Clicking the button increases the count.
import React, { useState, useCallback } from 'react'; function Counter() { const [count, setCount] = useState(0); const increment = useCallback(() => { setCount(c => c + 1); }, []); return ( <div> <p>Count: {count}</p> <button onClick={increment} aria-label="Increment count">Increase</button> </div> ); } export default Counter;
Using useCallback helps React avoid unnecessary work but don't overuse it; only use when you see performance issues or pass functions to optimized child components.
Remember to include all variables your function uses in the dependency array to keep it updated correctly.
useCallback keeps a function the same between renders unless dependencies change.
It helps improve performance by avoiding unnecessary function recreations.
Use it when passing functions to child components or hooks that depend on stable functions.
Practice
useCallback hook in React?Solution
Step 1: Understand what
useCallbackdoesuseCallbackreturns a memoized version of a function that only changes if its dependencies change.Step 2: Compare options with this behavior
Only To memoize a function so it doesn't get recreated on every render unless dependencies change describes memoizing a function to avoid recreating it unnecessarily.Final Answer:
To memoize a function so it doesn't get recreated on every render unless dependencies change -> Option AQuick Check:
useCallback = memoize function [OK]
- Confusing useCallback with useState
- Thinking useCallback stores values instead of functions
- Assuming useCallback fetches data
useCallback in a React functional component?Solution
Step 1: Recall the correct
The hook takes a function and an array of dependencies:useCallbacksignatureuseCallback(fn, [deps]).Step 2: Analyze each option
const memoizedFn = useCallback(() => { doSomething(); }, [dependency]);correctly passes an inline function and dependencies array.const memoizedFn = useCallback(() => { doSomething(); });misses dependencies array.const memoizedFn = useCallback(doSomething(), [dependency]);callsdoSomething()immediately, passing its return value instead of the function.const memoizedFn = useCallback(doSomething, dependency);passes dependency without array brackets, invalid.Final Answer:
const memoizedFn = useCallback(() => { doSomething(); }, [dependency]); -> Option CQuick Check:
useCallback(fn, [deps]) [OK]
- Omitting the dependencies array
- Passing dependencies without array brackets
- Passing function call instead of function reference
const MyComponent = () => {
const [count, setCount] = React.useState(0);
const increment = React.useCallback(() => {
setCount(count + 1);
}, [count]);
return <button onClick={increment}>Increment</button>;
};What happens when the button is clicked multiple times quickly?
Solution
Step 1: Understand the closure inside useCallback
Theincrementfunction usescountfrom its dependencies. Becausecountis in the dependency array, the function updates whencountchanges, but rapid clicks may use a stale value.Step 2: Analyze behavior on quick clicks
Rapid clicks may cause the function to use an outdatedcountvalue due to closure, causing increments to lag behind actual clicks.Final Answer:
The count increments but may lag behind due to stale closure -> Option DQuick Check:
useCallback with state dependency can cause stale closures [OK]
- Assuming count always updates instantly
- Ignoring closure behavior in hooks
- Thinking useCallback prevents all stale data issues
useCallback:const MyComponent = () => {
const [value, setValue] = React.useState(0);
const handleClick = React.useCallback(() => {
setValue(value + 1);
}, []);
return <button onClick={handleClick}>Click</button>;
};Solution
Step 1: Check the dependency array of useCallback
The callback usesvaluebut the dependency array is empty, so the function never updates with newvalue.Step 2: Understand the effect of missing dependencies
Becausevalueis missing, the callback uses the initialvalueforever, causing stale increments.Final Answer:
Missing dependencyvaluein the dependency array causes stale state updates -> Option AQuick Check:
Dependencies must include all used variables [OK]
- Leaving dependency array empty when variables are used
- Thinking setState functions need dependencies
- Confusing useCallback with useMemo
useCallback to achieve this?const Parent = () => {
const [count, setCount] = React.useState(0);
// Which is correct?
const handleClick = ???
return <Child onClick={handleClick} />;
};Solution
Step 1: Understand the goal to avoid unnecessary re-renders
We wanthandleClickto be stable and not change unless needed, so the child doesn't re-render unnecessarily.Step 2: Analyze each option's dependency array and function
const handleClick = React.useCallback(() => setCount(count + 1), [count]); depends oncount, so it changes every timecountchanges, causing re-renders. const handleClick = React.useCallback(() => setCount(c => c + 1), []); uses functional updatec => c + 1and empty dependencies, sohandleClickis stable. const handleClick = () => setCount(count + 1); recreates function every render. const handleClick = React.useCallback(() => setCount(count + 1)); misses dependencies array, which is invalid.Final Answer:
const handleClick = React.useCallback(() => setCount(c => c + 1), []); -> Option BQuick Check:
Stable callback with functional update and empty deps = const handleClick = React.useCallback(() => setCount(c => c + 1), []); [OK]
- Including state in dependencies causing new functions each render
- Not using functional updates when deps are empty
- Omitting dependency array causing bugs
Ready to prove it?
Pass the test and this pattern moves unknown
