React:使用 React.memo、useMemo 和 useCallback 优化组件
GenAI LIVE! | 2025年6月4日
本文最初发布于Headway 博客。访问headway.io,了解我们如何引领潮流。🏄♀️
大多数情况下,React 的性能并非您所需。核心库在后台做了大量工作,以确保所有内容都能高效渲染。然而,偶尔您可能会遇到组件渲染频率过高的情况,从而导致网站速度变慢。
让我们看一个例子:
const ListPage = ({data, title}) => (
<div>
<Header title={title}/>
<List listItems={data}/>
</div>
)
在这个例子中,任何对 的更改data
都会导致ListPage
重新渲染其所有子组件,包括Header
组件本身,即使它title
本身没有变化。Header
在相同的 props 下, 会渲染相同的结果,因此任何使用相同 props 的渲染都是不必要的。在这种情况下,这可能不是什么大问题,但如果<Header/>
每次渲染时都要执行一些昂贵的计算,我们希望确保它只在必要时进行渲染。
值得庆幸的是,有几种方法可以针对这种情况进行优化。
使用基于类的组件时,PureComponent
如果传入的 props 相同,则返回最后渲染的值。此外,还有一个shouldComponentUpdate
函数可以进行更精细的控制。使用函数式组件时,React 提供了三种优化方法,本文将重点介绍它们:React.memo
、useMemo
和useCallback
。
React.Memo
React.memo
是一个高阶组件,用于记忆函数组件的结果。如果一个组件在给定相同 props 的情况下返回相同的结果,那么将其包装进去memo
可以提升性能。以我们<Header/>
之前的例子为例。
假设它看起来像这样:
const Header = ({title}) => <h1>{title}</h1>
export default Header;
我们可以看到,除非发生变化,否则不需要渲染此组件title
,因此将其包装在内是安全的React.memo
。
const Header = ({title}) => <h1>{title}</h1>
export default React.memo(Header);
现在,无论何时Header
渲染,它都会对其 props 进行浅层比较。如果这些 props 相同,它将跳过渲染,而是返回上次渲染的值。
关于使用 React Dev Tools 的简要说明memo
。在撰写本文时,组件的包装方式如下……
const Header = React.memo(({title}) => <h1>{title}</h1>));
export default Header;
…会导致你的组件像在 React Dev Tools 中一样显示Unknown
。要解决这个问题,请在定义组件后将其包裹起来memo
,就像我们之前做的那样:
const Header = ({title}) => <h1>{title}</h1>;
export default React.memo(Header);
使用备忘录
useMemo
允许您记住函数的结果,并返回该结果直到依赖项数组发生变化。
例如:
const widgetList = useMemo(
() => widgets.map(
w => ({
...w,
totalPrice: someComplexFunction(w.price),
estimatedDeliveryDate: someOtherComplexFunction(w.warehouseAddress)
}),
),
[widgets],
);
在这个例子中,一个组件接收一个 Widget 列表。传入组件的 Widget 需要进行映射,以包含总价和预计送达日期,这需要使用一些复杂且开销很大的函数。如果该组件渲染成功,并且 的值widgets
保持不变,则无需再次运行这些开销很大的函数。
使用useMemo
将记住结果,因此如果widgets
自组件上次渲染以来没有改变,它将跳过函数调用并返回最后得到的结果。
使用回调
useCallback
可以防止父子组件之间不必要的渲染。
举个例子:
const Parent = () => {
const [showExtraDetails, setShowExtraDetails] = useState(false);
return (
[...]
<Child onClick={() => { showData(showExtraDetails); }/>
[...]
);
}
这个组件每次执行操作时都会导致Child
重新渲染Parent
,即使Child
是PureComponent
或者包裹在中React.memo
,因为onClick
每次渲染都会不同。useCallback
可以像这样处理这种情况:
const Parent = () => {
const [showExtraDetails, setShowExtraDetails] = useState(false);
const handleClick = useCallback(
() => {
showData(showExtraDetails);
},
[showExtraDetails],
);
return (
[...]
<Child onClick={() => {handleClick}/>
[...]
);
}
现在handleClick
将具有相同的值直到showExtraDetails
发生变化,这将减少渲染的次数Child
。
React 优化中需要考虑的事项
关于过早优化,需要注意的是。React 通常足够快,无需借助任何这些技术即可处理大多数用例。我建议您首先在不进行任何优化的情况下构建组件,并仅在必要时考虑添加性能增强。
了解更多资源
如果您想进一步探索这些 API,这里有一些资源可以帮助您更好地理解。
如果您希望进一步优化您的 React 应用程序,React 文档包含有关性能的重要部分。
鏂囩珷鏉ユ簮锛�https://dev.to/headwayio/react-optimize-components-with-react-memo-usememo-and-usecallback-39h8