Performance: useCallback hook
This affects interaction responsiveness by preventing unnecessary function recreations and re-renders in React components.
function Parent() { const [count, setCount] = React.useState(0); const handleClick = React.useCallback(() => { console.log('Clicked', count); }, [count]); return <Child onClick={handleClick} />; }
function Parent() { const [count, setCount] = React.useState(0); const handleClick = () => { console.log('Clicked', count); }; return <Child onClick={handleClick} />; }
| Pattern | DOM Operations | Reflows | Paint Cost | Verdict |
|---|---|---|---|---|
| Passing new function each render | Causes child re-render and possible DOM updates | Multiple reflows if child updates layout | Higher paint cost due to re-render | [X] Bad |
| Memoizing function with useCallback | Child re-renders only when dependencies change | Fewer reflows as layout stable | Lower paint cost due to fewer updates | [OK] Good |
useCallback hook in React?useCallback doesuseCallback returns a memoized version of a function that only changes if its dependencies change.useCallback in a React functional component?useCallback signatureuseCallback(fn, [deps]).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]); calls doSomething() immediately, passing its return value instead of the function. const memoizedFn = useCallback(doSomething, dependency); passes dependency without array brackets, invalid.const MyComponent = () => {
const [count, setCount] = React.useState(0);
const increment = React.useCallback(() => {
setCount(count + 1);
}, [count]);
return <button onClick={increment}>Increment</button>;
};increment function uses count from its dependencies. Because count is in the dependency array, the function updates when count changes, but rapid clicks may use a stale value.count value due to closure, causing increments to lag behind actual clicks.useCallback:const MyComponent = () => {
const [value, setValue] = React.useState(0);
const handleClick = React.useCallback(() => {
setValue(value + 1);
}, []);
return <button onClick={handleClick}>Click</button>;
};value but the dependency array is empty, so the function never updates with new value.value is missing, the callback uses the initial value forever, causing stale increments.value in the dependency array causes stale state updates -> Option AuseCallback to achieve this?const Parent = () => {
const [count, setCount] = React.useState(0);
// Which is correct?
const handleClick = ???
return <Child onClick={handleClick} />;
};handleClick to be stable and not change unless needed, so the child doesn't re-render unnecessarily.count, so it changes every time count changes, causing re-renders. const handleClick = React.useCallback(() => setCount(c => c + 1), []); uses functional update c => c + 1 and empty dependencies, so handleClick is stable. const handleClick = () => setCount(count + 1); recreates function every render. const handleClick = React.useCallback(() => setCount(count + 1)); misses dependencies array, which is invalid.Pass the test and this pattern moves unknown