useCallback(fn, deps)
is equivalent to useMemo(() => fn, deps)
.
useCallback |
useMemo |
|
---|---|---|
Function signature | const someCallback = useCallback(() => { expensivelyCompute(a) }, [a]); |
const someValue = useMemo(() => expensivelyCompute(a), [a]); |
What it does | someCallback will only change if a changes. An example usage is when someCallback is passed down as a prop. The child receiving someCallback will only re-render when a changes. |
someValue will only be re-computed if a changes. Essentially the same someValue will never be calculated twice in a row (assuming expensivelyCompute() is a pure function, of course). |
What it returns | A memoized callback | A memoized value |
When to use | Avoiding useless renders | Expensive data processing |
What if deps === undefined ? |
someCallback will be updated on every render. |
someValue will be re-computed on every render. |
Watch out for | N/A | The function () => expensivelyCompute(a) runs during rendering. Don't do any side effects in there (they belong in useEffect ). |
Reference: Kent C. Dodds
Object-like things are never strictly equal to each other.
{} === {} // false
[] === [] // false
// etc.
Therefore, when you put object-like things in a React dependency array, the re-render will always happen.
useMemo
and useCallback
fixes this:
function ComponentA() {
const memoizedFn = useCallback(() => {}, []);
const memoizedArr = useMemo(() => [], []);
useEffect(() => {
doStuff(memoizedFn, memoizedArr);
}, [memoizedFn, memoizedArr]);
// ...
}
useMemo
helps when an expensive value is being calculated every render:
function ComponentA({ input }) {
const memoizedValue = useMemo(() => expensivelyCalculate(input), [input]);
// ...
}
const MyButton = ({ handleClick }) => (
<button onClick={handleClick} />
);
// MyButton now uses shallow comparison.
// Any updates to the 'handleClick' function will be reflected inside <button>
export default React.memo(MyButton);