如何利用设置父级数组状态的函数实现子组件的记忆化(memoize)?
React子组件记忆化优化问题解决方案
问题背景
尝试对依赖父级状态且包含修改父级数组状态函数的子组件做记忆化,但遇到两个核心问题:
- 若将父级数组
state作为useCallback依赖,每次状态更新都会重建setA函数,导致所有兄弟子组件重渲染,记忆化效果失效 - 若不把
state加入依赖数组,会出现闭包问题,无法获取最新状态
初始代码:
const Parent = () => { const [state, setState] = useState([{},{},{}]); const setA = (index, a) => { let copyArr = [...state]; copyArr[index] = a; setState(copyArr); } return ( state.map((item, index)=>{ return <Child key={index} index={index} item={item} setA={setA} /> }) ) } const Child = ({index, item, setA}) => { return ( <View> <Button onClick={() => {setA(index, Math.floor(Math.random()*100))}} /> <Text>{item.a}</Text> </View> ) }
尝试的方案(效果不佳):
const Parent = () => { const [state, setState] = useState([{},{},{}]); const setA = React.useCallback((index, a) => { let copyArr = [...state]; copyArr[index] = a; setState(copyArr); }, [state]); return ( state.map((item, index)=>{ return <Child key={index} index={index} item={item} setA={setA} /> }) ) } const Child = React.memo(({index, item, setA}) => { return ( <View> <Button onClick={() => {setA(index, Math.floor(Math.random()*100))}} /> <Text>{item.a}</Text> </View> ) });
无需大幅重构的解决方案
方案1:使用setState的函数式更新消除对state的依赖
利用React setState的函数式更新特性,直接在setState里获取最新状态,这样useCallback的依赖数组可以为空,避免每次state更新都重建setA函数:
const Parent = () => { const [state, setState] = useState([{},{},{}]); // 使用函数式更新,依赖数组为空 const setA = React.useCallback((index, a) => { setState(prevState => { const copyArr = [...prevState]; copyArr[index] = {...copyArr[index], a}; // 浅拷贝原对象,避免直接覆盖丢失其他属性 return copyArr; }); }, []); // 依赖数组为空,setA只会在组件初始渲染时创建一次 return ( state.map((item, index)=>{ return <Child key={index} index={index} item={item} setA={setA} /> }) ) } const Child = React.memo(({index, item, setA}) => { return ( <View> <Button onClick={() => {setA(index, Math.floor(Math.random()*100))}} /> <Text>{item.a}</Text> </View> ) });
说明:
- 函数式更新里的
prevState是React传入的最新状态,完全避免闭包问题 useCallback依赖数组为空,setA函数不会随state更新重建- 子组件通过
React.memo实现记忆化,只有当item或index变化时才会重渲染,兄弟组件不会被牵连
方案2:给React.memo添加自定义比较函数(可选优化)
如果item是引用类型,默认的React.memo浅比较可能不够精准,可以自定义比较函数,只比较影响UI的属性:
const Child = React.memo(({index, item, setA}) => { return ( <View> <Button onClick={() => {setA(index, Math.floor(Math.random()*100))}} /> <Text>{item.a}</Text> </View> ) }, (prevProps, nextProps) => { // 仅当item.a、index或setA变化时才触发重渲染 return prevProps.index === nextProps.index && prevProps.item.a === nextProps.item.a && prevProps.setA === nextProps.setA; });
这个方案可以进一步减少不必要的重渲染,尤其适合item包含其他不影响UI的属性的场景。
内容的提问来源于stack exchange,提问作者Julian Chan
相关产品推荐
相关产品推荐

