Day8.|Day8. React中的setState及更新机制看这篇就够了
至关重要的知识点, 关系到能不能写出高效率的代码
为什么使用setState
- 开发中我们并不能直接通过修改state的值来让界面发生更新:
-
- 因为我们修改了state之后, 希望React根据最新的State来重新渲染界面, 但是这种方式的修改React并不知道数据发生了变化;
-
- React并没有实现类似于Vue2中的Object.defineProperty或者Vue3中的Proxy的方式来监听数据的变化;
-
- 我们必须通过setState来告知React数据已经发生了变化;
文章图片
修改了数据, 但是没有页面刷新, React不知道自己要刷新.png
文章图片
报了一个警告.png
this.setState({
counter : this.state.counter + 1
})
- 抛出来一个疑惑, 没有在类中定义过setState, 原因App类继承自Component, 继承了父类的方法, 回到源码里面看
源码.png
文章图片
image.png
- 异步更新, 不等待, 直接执行下面的代码
文章图片
改了文本, 打印的是Hello World.png - 为什么要设计成异步的?
文章图片
image.png
Rudux作者Dan Abramov的回答 - 简单的总结:
- setState设计为异步, 可以显著的提升性能;
-
- 如果每次调用setState都进行一次更新, 那么意味着render函数会被频繁的调用, 界面重新渲染, 这样效率很低;
-
- 最好的办法应该是获取到多个更新, 之后进行批量更新, 放入到一个队列里, 进行一个合并, 批量更新;
- 如果同步更新了state, 但是还没有执行render函数, 那么state和props不能保持同步;
-
- state和props不能保持一致性, 会在开发中产生很多的问题;
- 两种方式拿到最新的数据
// 方式一: 获取异步更新后的数据
// setState(更新的state, 回调函数)
this.setState({
message: "你好哇, 李银河"
}, () => {
console.log(this.state.message);
})// 方式二: 生命周期函数, 获取异步更新的state
componentDidUpdate() {
console.log(this.state.message);
}
有些情况下setState是同步更新的
- 定时器延迟0秒钟, 定时器函数是异步的,
文章图片
放在外面.png - 情况一: 将setState放入到定时器中
文章图片
放在里面变成了同步的代码.png
文章图片
情况二.png setState一定是异步吗?
文章图片
image.png
- 其实分成两种情况:
-
- 在组件生命周期或React合成事件中, setState是异步;
-
- 在setTimeout或者原生dom事件中, setState是同步;
- React框架不仅仅想要跑在浏览器里面, 也可以跑在原生里面, 原生控件对象, => 合成对象
文章图片
合成事件对象.png - 看源码, 看的最多的三个文件夹 react-reconclier react-dom react
- 不同的上下文, 不同的时间
源码.png
文章图片
返回同步处理或批量处理.png - 有一个优先级, 根据不同的值采取不同的处理方式
- 链表的数据结构, 一个节点连着一个节点
本质上是一个链表.png
- 什么叫做数据的合并? 思考一个问题, 只传入一个, 另一个会不会消失? 没有消失, 源码中做了操作
this.state = {
message: "Hello World",
name: coderwhy
}// 源码中的操作
Object.assign({}, this.state, {message: "你好哇, 李银河"})
- 了解源码更放心大胆的写代码, 使用API
- 了解真相你才能获得真正的自由
- 源码内部进行了合并, do while循环, 遍历了队列
- setState本身被合并
increment() {
this.setState({
counter: this.state.counter + 1
})
this.setState({
counter: this.state.counter + 1
})
this.setState({
counter: this.state.counter + 1
})
}
文章图片
源码, 每次合并都是一样的.png
- 不希望被合并可以怎么做?
- setState合并时进行累加
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
this.setState((prevState, props) => {
return {
counter: prevState.counter + 1
}
});
文章图片
把前一次的值传到里面.png React更新机制
- 我们在前面已经学习了React的渲染流程:
文章图片
渲染流程.png - React的更新流程
- props/state改变, render函数重新执行, 产生新的DOM数, diff算法把原来的DOM和新的DOM进行对比, 计算出差异进行更新, 更新到真实的DOM
文章图片
更新流程.png - React在props或state发生改变时, 会调用React的render方法, 会创建一棵不同的树.
- React需要基于这两棵不同的树之间的差别来判断如何有效的更新UI:
-
- 如果一棵树参考另外一棵树进行完全比较更新, 那么即使是最先进的算法, 改算法的时间复杂度为O(n3), 其中n是树中元素的数量;
文章图片
image.png
文章图片
两棵树比较的算法.png
- 如果一棵树参考另外一棵树进行完全比较更新, 那么即使是最先进的算法, 改算法的时间复杂度为O(n3), 其中n是树中元素的数量;
- 于是, React对这个算法进行了优化, 将其优化成了O(n)
-
- 同层节点之间相互比较, 不会跨节点比较, 只比较当前层;
-
- 不同类型的节点, 产生不同的树结构;
-
- 开发中, 可以通过key来指定那些节点在不同的渲染下保持稳定;
- 当节点为不同的元素, React会拆卸原有的数, 并且建立起新的树:
文章图片
image.png - 比如下面的代码更改:
文章图片
不会复用.png
文章图片
image.png
文章图片
image.png
文章图片
同类型的组件元素.png 情况三: 对子节点进行递归
- 在默认条件下, 当递归DOM节点的子元素使, React会同事遍历两个子元素的列表;
当产生差异时, 生成一个mutation.
文章图片
image.png
文章图片
对子节点进行递归.png
- 但是如果我们是在中间插入一条数据:
文章图片
image.png
文章图片
image.png keys的优化
- 我们在之前遍历一个列表时, 总是会提示一个警告??, 让我们添加一个key属性
文章图片
image.png
- 我们使用之前的一个嵌套案例:
-
- 在App中, 我们增加了一个计数器的代码
文章图片
image.png
- 在App中, 我们增加了一个计数器的代码
- 函数组件什么时候会被调用? 创建的时候会被调用一次,
- 调用setState时会执行render(), 需要做一个优化, 需要调用的时候调用
文章图片
其他的东西都重新render了, 浪费性能.png
- 点击添加按钮阻断过程, 实现一个生命周期函数
shouldComponentUpdate
, 默认返回true, 改成false, 不会阻止第一次渲染 - 应该是想要阻断的时候才阻断, 做一个判断
文章图片
做一个判断.png
- 一个项目里面有很多类, 每一个都需要做一个优化? 类组件特有的, 函数式组件如何优化?
文章图片
image.png
- 继承自PureComponent, 内部自动帮助做一件事情, 进行一个比较, 判断要不要重新调用
源码, 默认返回true.png
文章图片
浅层比较.png
文章图片
浅层比较源码.png
- 原因: 都继承自PureComponent, props没有改变, counter的值发生改变return true, render重新调用
- 开发中只需要做浅层比较就行了, 开发文档中提到过, 最好不要做深层比较, 太耗性能
文章图片
官方文档.png - 【Day8.|Day8. React中的setState及更新机制看这篇就够了】函数式组件还是每次调用, 如何解决?
文章图片
image.png
- 函数组件如何优化? 导入memo高阶组件, 对另一个组件进行操作
const MemoHeader = memo(function Header() {})
- ProductList也没有重新调用的原因, 上层Main组件继承自PureComponent
文章图片
少年~来做同学呀~.png
推荐阅读
- 理想中的生活
- 生活中的小幸福
- 人生中的第一次运动会,是一种什么样的体验()
- 拒绝做中的生活焦虑者
- 将基于Appsync的react程序发布到AWS|将基于Appsync的react程序发布到AWS S3
- 你记忆中的端午是什么样子的呢()
- 越长大陪伴的时间越少
- React笔记(组件)
- antd在线换肤定制功能
- 秋分(autumnalequinox)