时间:2023-11-01来源:系统城装机大师作者:佚名
react 一直遵循UI = fn(state) 的原则,有时候我们的state却和UI不同步 有时候组件本身在业务上不需要渲染,却又会再一次re-render。之前在项目中遇到的一些问题,这里做一个简单的分析,大家可以一起交流一下
react每次触发页面的更新可大致分成两步:
一般我们讨论的渲染 指的是第一步, 我可以悄悄的告诉你 第二步我们也管不了,什么时候更新真实DOM, React有一套自己的机制
组件渲染分为首次渲染和重渲染,首次渲染不可避免就不讨论 重渲染指当组件state或者props发生变化的时候造成的后续渲染过程,也是本文的讨论重点
其实React 在更新组件这方面 一直都有一个诟病 就是:
父组件重渲染的时候,会递归重渲染所有的子组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
const List = () => { const [name, setName] = useState<string>( "" ); // 用来测试的其它状态值 const [count, setCount] = useState<number>(0); const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; setName(val); }; const handleClick = () => { setCount((c) => c + 1); }; return ( <main> <div className= "list" > <input value={name} onChange={handleInputChange} /> <button onClick={handleClick}>测试</button> <Child count={count} /> </div> </main> ); }; const Child: React.FC<any> = (props) => { console.log( "Child has render" ); return <p>count:{props.count}</p>; }; |
当 Input name改变的时候 List触发rerender Child会发生rerender 可是Child 依赖的props只有count而已, 如果所有的子组件都被迫渲染,计算在render花费的时间和资源有可能成为性能瓶颈.
React其实刚出来就提供了优化的手段:
本文设计的组件以functioal component为主 因为后面会涉及到hooks的使用,对上述例子修改:
1 2 3 4 |
const Child: React.FC<any> = React.memo((props) => { console.log( "Child has render" ); return <p>count:{props.count}</p>; }) |
很好 child没有跟着name重渲染了,如果props是一个对象呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
const List = () => { const [name, setName] = useState<string>( "" ); // 用来测试的其它状态值 const [count, setCount] = useState<number>(0); console.log(count) const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const val = e.target.value; setName(val); }; const handleClick = () => { setCount((c) => c + 1); }; const item: IItem = { text: name, id: 1, }; return ( <main> <div className= "list" > <input value={name} onChange={handleInputChange} /> <button onClick={handleClick}>测试</button> <Child item={item} /> </div> </main> ); }; const Child: React.FC<{ count?: number; item: IItem }> = React.memo( ({ item }) => { console.log( "Child has render" ); return <p>text:{item.text}</p>; } ); |
改变name时候Child会改变 这是预期内的 而当改变count时,Child还是会重渲染,这是什么原因呢?因为count改变后 List组件会rerender 从而导致导致 item这个对象又重新生成了 导致child每次接受的是一个新的object对象 由于每个literal object的比较是引用比较 虽然前后属性相同,但比较得出的结果为false,造成 Child rerender 。
浅比较一定要相同引用吗?不一定,一般的面试中浅比较只是对值的比较 但是React.memo中要求引用类型一定要相同 为什么呢?我猜是出于对性能的考虑,不用深比较也是为了节约性能 通常情况下 我们想要的UI对应的是每个叶子节点的值 ,即只要叶子节点的值不发生变化 就不要rerender
方案:
还好React.memo有第二个参数可以使用
1 2 3 4 5 6 7 8 9 |
const Child: React.FC<{ item: IItem }> = React.memo( ({ item }) => { console.log( "Child has render" ); return <p>text:{item.text}</p>; }, (preProps, nextProps) => { return _.isEqual(preProps, nextProps); // lodash的深比较 } ); |
1 2 3 4 5 |
const item: MutableRefObject<IItem> = React.useRef({ text: name, id: 1, }); <Child item={item.current} /> |
好家伙,name无论怎么变化 Child 始终不会更新,useRef保证了返回的值是一个MutableObject 不可变的,意思就是引用完全相同 不管值变化 就不会保持更新.导致了UI不一致,那么我们怎么保证 name 不变的时候 item 和上次相等,name 改变的时候才和上次不等。useMemo
1 2 3 4 5 6 7 |
const item: IItem = React.useMemo( () => ({ text: name, id: 1, }), [name] // name变化触发item不等 name不变item和上次相同 ); |
useRef
returns a mutable ref object whose .current
property is initialized to the passed argumentinitialValue
). The returned object will persist for the full lifetime of the component.到此这篇关于React中immutable的使用的文章就介绍到这了,
2023-11-01
命令行清除Redis缓存的实现2023-11-01
Redis缓存空间优化实践详解2023-11-01
redis实现session共享的方法1.多次修改一个redis的String过期键,如何保证他仍然能保留第一次设置时的删除时间 2.修改hash、set、Zset、list的值,会使过期时间重置吗?...
2023-11-01
1、什么是延迟双删? 2、为什么要进行延迟双删? 3、对于方案处理都有弊端,那么我们需要使用延迟双删策略 4、需要注意的点 5、延迟的时间如何确定?...
2023-11-01