The key difference between React Native's useCallback and useMemo hooks lies in what they memoize and their intended use cases. Both hooks optimize performance by reducing unnecessary recalculations or recreations during component re-renders, but they do so in different ways.
useCallback is designed to memoize a function itself, providing a stable reference to the same function instance between renders unless its dependencies change. This is particularly useful when passing functions as props to child components to avoid triggering unnecessary re-renders due to function identity changes. For example, event handlers or callback functions can be wrapped in useCallback to maintain their reference stability.
In contrast, useMemo memoizes the result or return value of a function, caching the output of an expensive computation so that it only recalculates when its dependencies change. This is beneficial for expensive calculations, such as filtering or sorting data, that you don't want to perform on every render.
Although the APIs of both hooks are similarâthey both accept a function and a dependencies arrayâthe fundamental difference is that useCallback returns the memoized function itself, while useMemo returns the memoized result of executing the function. In fact, useCallback(fn, deps) is equivalent to useMemo(() => fn, deps) in terms of implementation, but their usage semantics differ.
Using these hooks incorrectly or excessively can degrade performance due to added memory usage, so they should be used judiciously for memoizing functions or values that are expensive to recreate or recalculate on every render.
Here is a detailed exploration of their differences, use cases, behavior, and performance implications:
Purpose and Behavior
useCallback is a hook that returns a memoized version of a callback function that only changes if one of its dependencies changes. The primary goal is to preserve the function reference between re-renders. This is crucial in React Native when functions are passed down as props to child components that rely on reference equality to prevent unnecessary renders. If the function changes on every render (which happens if you define functions inline), child components wrapped with React.memo will re-render unnecessarily.
useMemo, on the other hand, returns a memoized value. It accepts a function that performs an expensive calculation and returns its result, caching this result until one of the dependencies changes. This avoids repeating heavy computations on every render, thereby improving performance.
Use Cases
Use useCallback when:
- You have functions defined inside components that are passed as props to child components.
- You want to avoid passing new function references on every render that could trigger child component re-renders.
- You need a stable function reference for use in hooks like useEffect dependencies to prevent unnecessary effect executions.
Use useMemo when:
- You need to memoize the result of an expensive function.
- You want to avoid recalculating derived data unnecessarily.
- You deal with large datasets or expensive computations like sorting, filtering, or complex calculations that depend on component state or props.
Memoization and Dependencies
Both hooks accept a dependencies array, which controls when the memoized function or value should be recalculated. If any dependency changes between renders, the cached function (useCallback) or value (useMemo) is recalculated. If dependencies remain unchanged, the previous cached reference or result is reused.
Practical Examples
useCallback Example
Consider a button with an onClick handler inside a parent component:
js
const ParentComponent = ({ count }) => {
const handleClick = useCallback(() => {
console.log('Clicked', count);
}, [count]);
return ;
};
Without useCallback, handleClick would be recreated on every render, causing ChildComponent to re-render even if the props are otherwise unchanged.
useMemo Example
Consider a list of items you want to filter:
js
const FilteredList = ({ items, filterTerm }) => {
const filteredItems = useMemo(() => {
return items.filter(item => item.includes(filterTerm));
}, [items, filterTerm]);
return filteredItems.map(item => );
};
Here, the filtering only runs when items or filterTerm changes, avoiding expensive filtering on every render.
Performance Considerations
While both hooks improve performance by memoizing, they come with some trade-offs:
- Both use memory to store cached functions or values, so overusing them or using them unnecessarily can worsen performance.
- They are most beneficial in components with frequent re-renders or expensive computations.
- For simple functions or inexpensive calculations, using these hooks may add unnecessary complexity.
Summary of Differences
- useCallback memoizes the function itself, ensuring the same function instance is used unless dependencies change.
- useMemo memoizes the output of a function, recalculating it only when dependencies change.
- useCallback is typically used to stabilize functions passed as props.
- useMemo is typically used for memoizing expensive computation results.
- useCallback returns the function; useMemo returns the resolved value of the function.