使用 useCallback 与 useMemo 提升性能
使用 useCallback 记忆回调函数,避免子组件不必要的重新渲染,并使用 useMemo 缓存计算成本较高的值。
使用 useCallback 与 useMemo 提升性能 是 CoddyKit 上的免费 React Native Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Native Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Native Academy 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
The Re-Render Problem
Every time a component re-renders in React, all values created inside the function body are recreated. This includes objects, arrays, and most importantly — functions. If a recreated function or value is passed as a prop to a child component, the child sees a new reference and re-renders, even when the logical value has not changed.
What is useCallback?
useCallback(fn, deps) returns a memoized version of the callback function that only changes if one of the dependencies has changed. Wrapping a callback in useCallback preserves the same function reference across renders, preventing unnecessary re-renders in child components that receive it as a prop.
import { useCallback } from 'react';
function Parent() {
const [count, setCount] = useState(0);
// handlePress is the SAME function reference between renders
// as long as count does not change
const handlePress = useCallback(() => {
console.log('Pressed, count is:', count);
}, [count]);
return <Child onPress={handlePress} />;
}useCallback with React.memo
useCallback is most effective when paired with React.memo on the child component. Without React.memo the child re-renders anyway regardless of prop reference equality. The combination of React.memo on the child and useCallback on the parent callback prevents re-renders when neither the behavior nor the data has changed.
const ChildButton = React.memo(({ onPress, label }) => (
<Button title={label} onPress={onPress} />
));
function Parent() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
// ChildButton will NOT re-render when text changes
const handleCount = useCallback(() => setCount(c => c + 1), []);
return (
<>
<TextInput value={text} onChangeText={setText} />
<ChildButton label='Count' onPress={handleCount} />
</>
);
}What is useMemo?
useMemo(computeFn, deps) memoizes the result of an expensive computation. React calls computeFn on the first render and caches its return value. On subsequent renders it returns the cached value unless a dependency has changed. This avoids repeating heavy calculations every render.
import { useMemo } from 'react';
function ProductList({ products, category }) {
// Only recomputes when products or category changes
const filtered = useMemo(
() => products.filter((p) => p.category === category),
[products, category]
);
return <FlatList data={filtered} renderItem={renderItem} />;
}useMemo for Stable Object References
Objects and arrays created inline in JSX are new references on every render. If passed to a memoized child, they defeat memoization. Wrap them in useMemo to create a stable reference that only changes when the underlying data changes. This is especially important for style arrays and config objects.
function Chart({ data, color }) {
// New object on every render without useMemo
const chartConfig = useMemo(() => ({
backgroundColor: color,
dataPoints: data.map((d) => d.value),
}), [data, color]);
return <ChartComponent config={chartConfig} />;
}Dependency Arrays: Getting Them Right
Both useCallback and useMemo require accurate dependency arrays. Include every value from the component scope that is used inside the memoized function or computation. Missing a dependency causes the memoized value to use a stale closure; listing too many causes unnecessary recomputation. ESLint's exhaustive-deps rule helps enforce correctness.
// Wrong: missing userId dependency — stale closure bug
const fetchUser = useCallback(() => fetch('/user/' + userId), []);
// Correct: userId is listed
const fetchUser = useCallback(() => fetch('/user/' + userId), [userId]);When NOT to Use useCallback
useCallback has a cost: React stores the function in memory and checks the dependency array on every render. For simple handlers that are not passed to memoized children, the overhead of useCallback exceeds the benefit. Use it selectively — only when the child component is wrapped in React.memo and props equality matters for performance.
// Unnecessary: Button re-renders anyway; no child is memoized
const handlePress = useCallback(() => setVisible(true), []);
// Fine without useCallback in this case
const handlePress = () => setVisible(true);useMemo for Expensive Transformations
Use useMemo when a computation takes noticeable time — sorting large arrays, parsing complex data, running statistical calculations. The rule of thumb: if the computation takes more than a millisecond and runs on every render, consider memoizing it. For trivial operations (adding two numbers), the memoization overhead is larger than the computation itself.
const sortedAndFiltered = useMemo(() => {
return items
.filter((item) => item.status === selectedStatus)
.sort((a, b) => b.createdAt - a.createdAt)
.slice(0, 100);
}, [items, selectedStatus]);useCallback for renderItem in FlatList
Wrapping the renderItem function of a FlatList in useCallback prevents a new function reference from being created on every parent render, which would cause every visible FlatList row to re-render. This is one of the most impactful performance wins in list-heavy React Native apps.
const renderItem = useCallback(({ item }) => (
<ProductCard
product={item}
onPress={handleSelectProduct}
/>
), [handleSelectProduct]); // handleSelectProduct also memoized
<FlatList
data={products}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>Measuring the Impact
Before adding memoization everywhere, measure the actual impact using the React DevTools Profiler. Record an interaction, look at the flame chart to identify components that re-render unnecessarily, and only add useCallback or useMemo where the profiler shows a real bottleneck. Premature optimization adds code complexity with no measurable benefit.
Summary Comparison
Remember the key difference: useCallback memoizes a function (prevents recreating the function reference), while useMemo memoizes a computed value (prevents re-running an expensive calculation). Both accept a dependency array and only recompute when the listed dependencies change.
// useCallback — memoize a function
const fn = useCallback(() => doSomething(a, b), [a, b]);
// useMemo — memoize a computed value
const val = useMemo(() => computeExpensive(a, b), [a, b]);
// Relationship: useCallback(fn, deps) === useMemo(() => fn, deps)Quick Check
Test your understanding of useCallback and useMemo for performance from this lesson.
Lesson Recap
In this lesson you learned: useCallback memoizes a function reference to prevent unnecessary child re-renders, useMemo memoizes an expensive computed value to skip re-calculation on every render, and both work best when combined with React.memo and accurate dependency arrays. Next up we explore writing custom hooks to share stateful logic between components.
常见问题解答
「使用 useCallback 与 useMemo 提升性能」课时是免费的吗?
是的 — 「使用 useCallback 与 useMemo 提升性能」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Native Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Native Academy 课程共包含 4 节课。
「使用 useCallback 与 useMemo 提升性能」这节课中我会学到什么?
使用 useCallback 记忆回调函数,避免子组件不必要的重新渲染,并使用 useMemo 缓存计算成本较高的值。 你通过在浏览器中直接运行的动手代码来练习 React Native Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 React Native Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 React Native Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「使用 useCallback 与 useMemo 提升性能」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 React Native Academy 课中编写并运行代码吗?
能。每节 React Native Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 使用 useRef 引用 DOM 节点与可变值
- 使用 useReducer 处理复杂状态逻辑
- 使用 useCallback 与 useMemo 提升性能
- 编写自定义钩子