React中如何避免useState默认状态的不必要计算?
解决方案
核心优化:使用useState的初始化函数
针对你不想让复杂初始计算每次渲染都执行、又不想用useEffect的需求,React官方提供了直接的解决方案——给useState传入初始化函数,而非直接传入计算结果。
修改后的代码如下:
import React, { useState } from 'react'; import _ from 'lodash'; type MyRecord = { id: string; // 20 more props... }; export default function MyComponent({ records }: { records: Array<MyRecord> }) { const [lastPageLoadedIndex, setLastPageLoadedIndex] = useState(0); // 将复杂计算逻辑包裹在函数中传给useState const [pages, setPages] = useState(() => _.chunk(records, 10).map((chunk, i) => ({ records: chunk, loaded: i === 0, })) ); // 修复直接修改state的问题,改用不可变更新方式 const handleClick = () => { setLastPageLoadedIndex(prev => prev + 1); setPages(prevPages => { const nextIndex = lastPageLoadedIndex + 1; if (nextIndex >= prevPages.length) return prevPages; return prevPages.map((page, idx) => idx === nextIndex ? { ...page, loaded: true } : page ); }); }; return ( <div> {pages .flatMap((page) => page.loaded ? page.records.map((record) => ( <div key={`record-${record.id}`}> {record.id} </div> )) : [] )} <button onClick={handleClick} disabled={lastPageLoadedIndex + 1 >= pages.length} >Load next</button> </div> ); }
为什么这个方案可行?
- React的
useState规则:如果传入的是一个函数,这个函数只会在组件首次渲染时执行一次,返回值作为初始state;后续渲染时会直接跳过这个函数,不会重复计算,完美避免了冗余执行。 - 不需要依赖
useEffect,组件只会渲染一次初始状态,不会有额外的二次渲染,对SEO友好。
关于你提到的useMemo方案
用useMemo缓存初始值再传入useState不是理想方案:
useMemo的作用是缓存计算结果,依赖变化时会重新执行计算;但你的需求是仅初始化时计算一次,useState的初始化函数本身就具备这个特性,不需要额外的useMemo。- 如果
records是动态变化的,useMemo会在records更新时重新计算,但这时候你可能需要的是根据新的records重置state,而非仅初始化,这种场景下才需要结合useEffect,但这和你最初的需求不符。
额外优化点
- 避免直接修改state:原代码中直接修改
pages数组的做法违反了React的不可变状态原则,改用基于prev state的不可变更新(如map或展开运算符),确保React能正确检测状态变化。 - 简化渲染逻辑:用
flatMap替代map+flat,代码更简洁。 - 按钮禁用逻辑:添加
disabled属性,防止用户点击超出页数的按钮。
内容的提问来源于stack exchange,提问作者Alien
相关产品推荐
相关产品推荐

