在构建 React 应用时,性能优化是提升用户体验的关键。你是否遇到过组件频繁、不必要的重渲染,导致应用卡顿?一个常见的元凶就是:在渲染过程中创建新的引用值(函数或对象)作为 props 传递给子组件。
问题根源:
React 默认使用浅比较 (shallow comparison) 来判断子组件是否需要更新。当你像这样传递回调函数给子组件:
function ParentComponent() {
const handleClick = () => { /* ... */ }; // 每次渲染都创建新函数
return <ChildComponent onClick={handleClick} />;
}
或者传递一个新创建的对象/数组:
function ParentComponent({ items }) {
const processedItems = items.map(item => ({ ...item, extra: calcExtra(item) })); // 每次渲染都创建新数组
return <ChildComponent data={processedItems} />;
}
每次 ParentComponent 渲染,handleClick 和 processedItems 都会在内存中创建一个全新的引用。即使它们的逻辑或内容没变,子组件 (ChildComponent) 接收到的 onClick 或 data prop 在引用上发生了变化,这会触发子组件的重渲染,即使它本不需要更新!
解决方案:useCallback & useMemo
React 提供了两个 Hook 来缓存这些引用,避免在依赖项未变化时创建新引用:
useCallback: 缓存函数引用import React, { useCallback } from 'react'; function ParentComponent() { const handleClick = useCallback(() => { // 函数逻辑 }, []); // 依赖项数组:空数组表示只在组件挂载时创建一次 return <ChildComponent onClick={handleClick} />; }只有当依赖项数组中的值发生变化时,
useCallback才会返回一个新的函数引用。否则,返回之前缓存的函数。useMemo: 缓存计算结果(对象/数组/任何值)import React, { useMemo } from 'react'; function ParentComponent({ items }) { const processedItems = useMemo(() => { return items.map(item => ({ ...item, extra: calcExtra(item) })); }, [items]); // 依赖项:当 `items` 变化时才重新计算 return <ChildComponent data={processedItems} />; }useMemo仅在依赖项变化时才重新执行传入的函数并返回新的计算结果,否则返回缓存的值。
关键点:
- 依赖项数组 (
deps) 至关重要: 必须准确列出所有影响函数或计算结果的变量。遗漏依赖项会导致缓存了过时的值,引入 bug;过度添加则可能失去优化效果。 - 不要滥用: 并非所有函数和计算都需要缓存。仅在它们被传递给子组件(尤其是
React.memo优化的组件)或作为其他 Hook(如useEffect)的依赖项,且创建成本较高时使用。过度使用反而会增加内存和计算开销。
总结:
善用 useCallback 和 useMemo 是优化 React 应用性能的利器,能有效阻止因引用变化导致的无效子组件重渲染。牢记它们的适用场景和正确设置依赖项,能让你的应用运行更加丝滑流畅!