这篇文章主要介绍了React中immutable的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
react 一直遵循UI = fn(state) 的原则,有时候我们的state却和UI不同步 有时候组件本身在业务上不需要渲染,却又会再一次re-render。之前在项目中遇到的一些问题,这里做一个简单的分析,大家可以一起交流一下
UI组件渲染性能
react每次触发页面的更新可大致分成两步:
- render(): 主要是计算v-dom的diff
- commit阶段 :将得到的diff v-dom一次性更新到真实DOM
一般我们讨论的渲染 指的是第一步, 我可以悄悄的告诉你 第二步我们也管不了,什么时候更新真实DOM, React有一套自己的机制
组件渲染分为首次渲染和重渲染,首次渲染不可避免就不讨论 重渲染指当组件state或者props发生变化的时候造成的后续渲染过程,也是本文的讨论重点
其实React 在更新组件这方面 一直都有一个诟病 就是:
父组件重渲染的时候,会递归重渲染所有的子组件
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花费的时间和资源有可能成为性能瓶颈.
方案一:shallow compare
React其实刚出来就提供了优化的手段:
- shouldComponentUpdate: 返回false 就直接跳过组件的render过程
- React.PureComponent: 对props进行浅比较,如果相等 则跳过render 用于class 组件
- React.memo: 也是进行浅比较,适用于functional Component
本文设计的组件以functioal component为主 因为后面会涉及到hooks的使用,对上述例子修改:
const Child: React.FC<any> = React.memo((props) => {
console.log("Child has render");
return <p>count:{props.count}</p>;
})
很好 child没有跟着name重渲染了,如果props是一个对象呢?
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
方案:
- 直接对前后的对象进行deepCompare
还好React.memo有第二个参数可以使用
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的深比较
}
);
- 保证引用相等的情况下,值也相等 useRef
const item: MutableRefObject<IItem> = React.useRef({
text: name,
id: 1,
});
<Child item={item.current} />
好家伙,name无论怎么变化 Child 始终不会更新,useRef保证了返回的值是一个MutableObject 不可变的,意思就是引用完全相同 不管值变化 就不会保持更新.导致了UI不一致,那么我们怎么保证 name 不变的时候 item 和上次相等,name 改变的时候才和上次不等。useMemo
const item: IItem = React.useMemo(
() => ({
text: name,
id: 1,
}),
[name] // name变化触发item不等 name不变item和上次相同
);
总结:
- 父组件重渲染的时候,会递归重渲染所有的子组件
- 对primitive 值的数据 React比较值的相等来判断是否重渲染组件 对Object数据 React比较引用 如果引用相同 不会重渲染,如果引用不同 会认为是不同对象 造成重渲染
- useRef返回一个MutableRefObject数据 永远返回的是同一个引用 直到生命周期结束,官网的注解
useRef
returns a mutable ref object whose.current
property is initialized to the passed argument
- (
initialValue
). The returned object will persist for the full lifetime of the component.
- (
- useMemo 返回一个计算的值 当dep改变时 返回的值才改变(引用的改变)
到此这篇关于React中immutable的使用的文章就介绍到这了,更多相关React immutable内容请搜索编程学习网以前的文章希望大家以后多多支持编程学习网!
本文标题为:React中immutable的使用
- Mongodb启动报错完美解决方案:about to fork child process,waiting until server is ready for connections. 2023-07-16
- Oracle 删除大量表记录操作分析总结 2023-07-23
- 基于Python制作一个简单的文章搜索工具 2023-07-28
- MySQL8.0.28安装教程详细图解(windows 64位) 2023-07-26
- 搭建单机Redis缓存服务的实现 2023-07-13
- SQLSERVER调用C#的代码实现 2023-07-29
- Numpy中如何创建矩阵并等间隔抽取数据 2023-07-28
- SQL Server 2022 AlwaysOn新特性之包含可用性组详解 2023-07-29
- 在阿里云CentOS 6.8上安装Redis 2023-09-12
- redis清除数据 2023-09-13