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
后端开发教程 - Java、Spring Boot 实战 - msg200.com
