Suggest an editImprove this articleRefine the answer for “What is the difference between useCallback and useMemo?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**useCallback** returns a memoized function reference; **useMemo** returns a memoized computed value. `useCallback(fn, deps)` is equivalent to `useMemo(() => fn, deps)`. ```jsx const doubled = useMemo(() => count * 2, [count]); // caches value const handleClick = useCallback(() => setCount(c => c + 1), []); // caches function ref ``` **Key point:** use `useMemo` for expensive calculations, `useCallback` for stable function props passed to memoized child components.Shown above the full answer for quick recall.Answer (EN)Image**useCallback** memoizes a function reference; **useMemo** memoizes a computed value. Same dep-array mechanic, different return type. ## Theory ### TL;DR - `useMemo` caches a computed value: filter a list once, reuse the result until deps change - `useCallback` caches a function reference: same function object every render unless deps change - Core insight: `useCallback(fn, deps)` is literally `useMemo(() => fn, deps)` - Need a stable function ref for a child prop? `useCallback`. Need an expensive computed value? `useMemo` - For cheap operations, skip both. Hook overhead adds up fast. ### Quick example ```jsx import { useState, useMemo, useCallback } from 'react'; function Counter() { const [count, setCount] = useState(0); const [other, setOther] = useState(0); // useMemo: recomputes only when count changes const doubled = useMemo(() => count * 2, [count]); // useCallback: same function reference unless deps change const handleClick = useCallback(() => setCount(c => c + 1), []); return <button onClick={handleClick}>Count: {doubled} | other: {other}</button>; } ``` Change `other` and `doubled` does not recompute. Change `count` and `doubled` recalculates, but `handleClick` stays the same reference. ### Key difference `useMemo` runs your factory and stores whatever it returns: a number, string, object, array. `useCallback` stores the factory function itself without calling it. So if your factory returns a function, `useMemo` gives you a new function reference each time (unstable), while `useCallback` gives you the stable reference you actually need for [React.memo](/questions/react-memo) child optimization. ### When to use - Filtering or sorting a large array on every render? `useMemo` on the result. - Passing a callback as a prop to a `React.memo` child? `useCallback` to keep the reference stable. - Inline function in a [useEffect dep array](/questions/useeffect)? `useCallback` to prevent infinite loops. - `count * 2` or `name.toUpperCase()`? Neither. Compute inline. ### Comparison table | Aspect | useMemo | useCallback | |---|---|---| | Returns | Computed value (any type) | Function reference | | Factory runs | Only when deps change | Only when deps change | | Primary use | Expensive calculations | Stable callback props | | With React.memo | Memoize object/array props | Memoize function props | | Equivalent to | `useMemo(() => val, deps)` | `useMemo(() => fn, deps)` | | When to skip | Cheap operations | Function not passed to any child | ### How it works internally React stores the memoized result in the fiber node's `memoizedState`. On each re-render it runs a shallow comparison (`Object.is`) on each dep. If all deps match, it returns the cached value or function without calling the factory. This is why object or array deps cause constant recomputes: `{}` never equals `{}` by reference. ### Common mistakes **Missing deps (stale closure):** ```jsx // Wrong: b is used but not in deps const sum = useMemo(() => a + b, [a]); // Always a + 2, ignores b updates // Fix const sum = useMemo(() => a + b, [a, b]); ``` `eslint-plugin-react-hooks` with `exhaustive-deps` catches this automatically. Add it to every React project. **useCallback when no child receives the function:** ```jsx // Pointless: handleClick is not passed anywhere as a prop const handleClick = useCallback(() => setCount(c => c + 1), []); return <button onClick={handleClick}>Click</button>; ``` I've seen codebases where `useCallback` was added to every handler after someone read an optimization guide. Profiler showed no difference, just extra overhead and harder-to-spot dep bugs. If the function is not passed to a memoized child or used in a dep array, inline it. **Object or array in deps without memoization:** ```jsx // Recomputes every render: options is a new reference each time const result = useMemo(() => compute(options), [options]); // Fix: memoize the dep too const stableOptions = useMemo(() => ({ limit: 10 }), []); const result = useMemo(() => compute(stableOptions), [stableOptions]); ``` ### Real-world usage - **Search/filter UI:** `useMemo` to filter 1000+ items without re-filtering on unrelated state changes - **TanStack Query:** wraps query results in `useMemo` for stable references - **Custom hooks:** `useCallback` on returned functions so consumers don't re-render unnecessarily - **Redux selectors:** stable action creators via `useCallback` patterns ### Follow-up questions **Q:** What happens if you put an object literal directly in the dep array? **A:** Shallow compare fails every render. `{} === {}` is `false`, so the hook recomputes constantly. Wrap the object in its own `useMemo` or destructure it to primitives. **Q:** If `useCallback(fn, deps)` equals `useMemo(() => fn, deps)`, why does React have both? **A:** Readability. `useCallback` signals intent: this is a stable callback. `useMemo` signals: this is a cached value. Same engine, different semantics. **Q:** Can memoization make performance worse? **A:** Yes. Each hook call allocates memory and runs dep comparisons. For trivial operations like `count * 2`, that overhead beats the cost of just recalculating. Measure with React DevTools Profiler before adding memos. **Q:** Why does the factory run twice in React 18 Strict Mode? **A:** React intentionally double-invokes factories in dev to surface side effects in code that should be pure. Production runs it once. **Q:** (Senior) How does React 19 change this picture? **A:** The React Compiler can automatically insert memoization where it helps, reducing the need to write `useMemo` and `useCallback` by hand. The underlying mechanism stays the same. ## Examples ### Filtering a list with useMemo and useCallback ```jsx function TodoList({ todos, filter }) { // Without useMemo: re-filters all todos on every parent re-render const visibleTodos = useMemo(() => todos.filter(todo => todo.text.toLowerCase().includes(filter.toLowerCase()) ), [todos, filter] ); // Stable ref: memoized TodoItem children skip re-renders const handleDelete = useCallback((id) => { console.log('Deleting todo', id); }, []); return visibleTodos.map(todo => ( <TodoItem key={todo.id} todo={todo} onDelete={handleDelete} /> )); } ``` `visibleTodos` recomputes only when `todos` or `filter` changes. `handleDelete` keeps the same reference, so memoized `TodoItem` children skip re-renders when the parent updates for unrelated reasons. ### Stale closure bug in deps ```jsx function BadSum() { const [a, setA] = useState(1); const [b, setB] = useState(2); // Bug: b is missing from deps const sum = useMemo(() => a + b, [a]); // After b updates to 3, sum still shows a + 2 return ( <> <button onClick={() => setB(b => b + 1)}>B: {b}</button> <div>Sum: {sum}</div> </> ); } ``` Clicking B increments `b` in state, but the memo stays stale because `b` is not in the dep array. Fix: `[a, b]`. The `exhaustive-deps` lint rule flags this the moment you save the file.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.