深入理解setstate
本文会向你介绍以下内容:
- 如何使用 setState
- 不能直接修改 State
- setState()
- setState 可能是异步更新
- setState 的合并
如何使用 setState
在介绍 setState 之前,我们先来看一个 setState 的案例,了解一下是如何使用的。
我们来展示一个使用案例,当点击一个 改变文本 的按钮时,修改界面前显示的内容:
![](https://img-blog.csdnimg.cn/img_convert/15bb6dff9a2bfc1f1aac45ea350eabbd.webp?x-oss-process=image/format,png#clientId=ubecc856d-2fbf-4&from=paste&id=ud3a5056c&margin=[object Object]&originHeight=140&originWidth=140&originalType=url&ratio=1&status=done&style=none&taskId=u43336020-69bc-45f9-9e7c-41339f8aede)
案例代码
import React, { Component } from 'react'export default class App extends Component {constructor(props) {super(props)this.state = {message: 'Hello React',}}render() {return (<div><h2>{this.state.message}</h2><button onClick={(e) => this.changeMessage()}>ChangeMessage</button></div>)}changeMessage() {this.setState({message: 'Hello xhs,your message is changed.',})}
}
state 的初始化是在类构造器中去设置的,然后如果想要更改 state ,那就是通过 setState 函数,该函数最主要的就是传入一个对象作为参数,而该对象就是你想要修改的值。上述代码中,当你点击 ChangeMessage 时,就会调用 setState 函数,而 setState 会调用 render 函数,页面就会被重新渲染。
点击按钮之后,重新渲染的效果:
不能直接修改 State
将上面的 changeMessage 方法,改成下面的样子。
changeMessage() {this.state.message = "Hello xhs,your message is changed.";
}
点击 ChangeMessage 之后,页面并没有改变,但是打印 state 你会发现,state 中的 message 变了,但是页面不会被重新渲染。
构造函数,是我们唯一可以给 this.state 赋值的地方,而为了可以重新渲染页面,那就只能通过 setState 函数来修改。
所以,我们通过调用 setState 来修改数据:
- 当我们调用 setState 时,会重新执行 render 函数,根据最新的 State 来创建 ReactElement 对象
- 再根据最新的 ReactElement 对象,对 DOM 进行修改;
changeMessage() {this.setState({message: "Hello xhs,your message is changed."})
}
setState()
setState(updater, [callback])
setState() 将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式
将 setState() 视为 请求 而不是立即更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。React 并不会保证 state 的变更会立即生效。
setState() 并不总是立即更新组件。它会批量推迟更新。这使得在调用 setState() 后立即读取 this.state 成为了隐患。为了消除隐患,请使用 componentDidUpdate 或者 setState 的回调函数(setState(updater, callback)),这两种方式都可以保证在应用更新后触发。
参数一可以有两种形式
1. 接受对象类型
setState(stateChange[, callback])
stateChange 会将传入的对象浅层合并到新的 state 中,例如 让 count +1
this.setState({ count: this.state.count + 1 })
这种形式的 setState() 也是异步的,并且在同一周期内会对多个 setState 进行批处理。例如,如果在同一周期内多次 count + 1,则相当于:
Object.assign(previousState, // 之前的状态{count: state.count + 1},{count: state.count + 1},...
)
后调用的 setState() 将覆盖同一周期内先调用 setState 的值,因此商品数仅增加一次。如果后续状态取决于当前状态,我们建议使用 updater 函数 的形式代替:
this.setState((state) => {return { count: state.count + 1 }
})
2. 接受函数参数
我们在初始的 state 中并没有一个 count 。但是现在我们有一个需求:那就是添加一个 count 在 state 中,使用对象作为第一参数,你就会发现这样一个问题。
changeCount () {this.setState({count: 0}) // => this.state.count 是 undefinedthis.setState({count: this.state.count + 1}) // => undefined + 1 = NaNthis.setState({count: this.state.count + 2}) // => NaN + 2 = NaN
}
上面的代码的运行结果并不能达到我们的预期,我们希望 count 运行结果是 3 ,可是最后得到的是 NaN。但是这种后续操作依赖前一个 setState 的结果的情况并不罕见。
这里就自然地引出了 setState 的第二种使用方式,可以接受一个函数作为参数。React.js 会把上一个 setState 的结果传入这个函数,你就可以使用该结果进行运算、操作,然后返回一个对象作为更新 state 的对象:
changeCount () {this.setState((prevState) => {return { count: 0 }})this.setState((prevState) => {return { count: prevState.count + 1 }// 上一个 setState 的返回是 count 为 0,这里需要执行 +1 所以当前返回 1})this.setState((prevState) => {return { count: prevState.count + 2 }// 上一个 setState 的返回是 count 为 1,这里需要执行 +2 所以当前返回 3})// 最后的结果是 this.state.count 为 3
}
这样就可以达到上述的_利用上一次 setState 结果进行运算_的效果。
参数二为回调函数
setState() 的第二个参数为可选的回掉函数,它将在 setState 完成合并并重新渲染组件后执行。通常,我们建议使用 componentDidUpdate() 来代替此方式。
我们来给案例中的 changeMessage 函数添加两个打印方式。
changeMessage() {this.setState({message: "Hello xhs,your message is changed."},() => {console.log('callback result: ',this.state.message);})console.log('no callback result:',this.state.message);
}
我们来看看结果
从图片中我们可以看出
- 在 setState 外部的时候,并不可以立马拿到我们想要的结果
- callback 中则返回的是修改之后的结果。
正因为不是立马拿到我们想要的结果,所以这就是我们接下来要讲的 setState 可能是异步更新。
这样我们就可以知道 setState 的第二参数的作用,就是可以确保得到 state 已经修改之后的结果。也就是重新渲染之后执行的内容。
setState 可能是异步更新
我们来看下面的代码:
- 最终打印结果是 Hello React;
- 可见 setState 是异步的操作,我们并不能在执行完 setState 之后立马拿到最新的 state 的结果
changeMessage() {this.setState({message: "Hello xhs,your message is changed."})console.log(this.state.message); // Hello React
}
为什么 setState 设计为异步呢?
- setState 设计为异步其实之前在 GitHub 上也有很多的讨论;
- React 核心成员(Redux 的作者)Dan Abramov 也有对应的回复[1],可以参考一下;
我们来对他的回答做一个简单的总结:
- setState 设计为异步,可以显著的提升性能;
- 如果每次调用 setState 都进行一次更新,那么意味着 render 函数会被频繁调用,界面重新渲染,这样效率是很低的;
注意: 最好的办法应该是获取到多个更新,之后进行批量更新;
- 如果同步更新了 state ,但是还没有执行 render 函数,那么 state 和 props 不能保持同步;
注意: state 和 props 不能保持一致性,会在开发中产生很多的问题;
获取到更新后的值
- setState 的第二参数,一个回调函数,这个回调函数会在更新后会执行;
changeMessage() {this.setState({message: "Hello xhs,your message is changed."}, () => {console.log(this.state.message); // Hello xhs,your message is changed.});
}
当然,我们也可以在生命周期函数:
componentDidUpdate(prevProps, provState, snapshot) {console.log(this.state.message);
}
setState 一定是异步?
疑惑:setState一定是异步更新的吗?
验证一:在 setTimeout 中的更新:
changeText() {setTimeout(() => {this.setState({message: "你好啊,小和山"});console.log(this.state.message); // 你好啊,小和山}, 0);
}
验证二:原生 DOM 事件:
componentDidMount() {const btnEl = document.getElementById("btn");btnEl.addEventListener('click', () => {this.setState({message: "你好啊,小和山"});console.log(this.state.message); // 你好啊,小和山})
}
其实分成两种情况:
- 在组件生命周期或 React 合成事件中,setState 是异步;
- 在 setTimeout 或者原生 dom 事件中,setState 是同步;
setState 的合并
数据的合并
当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state
给 state 定义一些数据
this.state = {name: 'xhs',message: 'Hello React',count: 0,
}
通过 setState 去修改 state 中的 message ,是不会对 name 产生影响的
changeMessage() {this.setState({message: "Hello xhs,your message is changed."});
}
多个 setState 会被合并
比如我们还是有一个 count 属性,默认为 0,记录当前的数字:
- 执行下面的操作之后,最后的答案是 1
- 多个 state 进行了合并
increment() {this.setState({count: this.state.count + 1});this.setState({count: this.state.count + 1});this.setState({count: this.state.count + 1});
}
如何可以做到,让 count 最终变成 3 呢?
increment() {this.setState((state, props) => {return {count: state.count + 1}})this.setState((state, props) => {return {count: state.count + 1}})this.setState((state, props) => {return {count: state.count + 1}})
}
上面的代码就是用到了,setState 接收一个函数作为第一参数的情况,来解决这个问题,就可以很好的得到我们期待的结果。
参考资料
对应的回复: https://github.com/facebook/react/issues/11527#issuecomment-360199710
深入理解setstate相关推荐
- 理解setState(),异步还是同步?
state state的存在是为了动态改变组件,比如根据不同的用户操作和网络请求,来重新渲染组件. setState()是React给我们的一个API,用来改变或定义state. setState() ...
- 函数setState是React的未来
by Justice Mba 由Mba法官 函数setState是React的未来 (Functional setState is the future of React) Update: I gav ...
- 前端React教程第六课 认识栈调和、setState和Fiber架构
10 React 中的"栈调和"(Stack Reconciler)过程是怎样的? 时下 React 16 乃至 React 17 都是业界公认的"当红炸子鸡" ...
- 从 Dropdown 的 React 实现中学习到的
Demo Demo Link Note dropdown 是一种很常见的 component,一般有两种: 展开 dropdown menu 后,点击任意地方都应该收起 menu. 展开 dropdo ...
- react textarea 空格为什么不换行_你需要的 React + TypeScript 50 条规范和经验
这篇文章没有对错之分,肯定也有不完善的地方,结合了自己日常开发和经验.可以让你书写代码更具严谨性,希望看完之后有所帮助.本文字数4000+ ,看完本文大概需半小时. 1. 注释 (1) 文件顶部的注释 ...
- react 16 对外暴露function_【第 25 期】React 架构的演变 从同步到异步(一)
正文开始~ 写这篇文章的目的,主要是想弄懂 React 最新的 fiber 架构到底是什么东西,但是看了网上的很多文章,要不模棱两可,要不就是一顿复制粘贴,根本看不懂,于是开始认真钻研源码.钻研过程中 ...
- react 显示当前时间_React 灵魂 23 问,你能答对几个?
1.setState 是异步还是同步? 合成事件中是异步 钩子函数中的是异步 原生事件中是同步 setTimeout中是同步 相关链接: 你真的理解setState吗? 2.聊聊 react@16.4 ...
- 举些例子看看一个程序员的水平究竟可以差到什么程度?
闲话少说直接入正题 你敢信年入百万人民币的白左能精雕细琢出这样的垃圾么(见下图) .这是16年做了五六年的情况,现在20年了,听说 终于能跑hello world了. 某西海岸IT大厂,产品失败就开源 ...
- 阿里P10赵海平跳槽字节跳动:深度解析跳槽从开始到结束完整流程!
00. 前言 近期,有消息称,前阿里P10员工赵海平已加入字节跳动,职级为4+.王垠加入华为职级为 21级. 事情起因还是从两人乌龙说起,详细经过请看:王垠受邀面试阿里P9,被P10面跪后网上怒发文, ...
最新文章
- windows游戏编程_少儿编程该怎么学?看看帕拉卡3D动画编程创始人李西峙分享七个核心...
- Storage与cookie
- 【Python】Python获取命令行參数
- UVA 10055(Hashmat the brave warrior )
- ORACLE 多表关联 UPDATE 语句
- 打开新经济大门 淘宝直播如何打造最有价值的直播平台?
- 迷你世界无限迷你币体验服务器,迷你世界体验服无限迷你币
- C语言求一个文件的长度,求二进制文件的长度
- jquery,6位密码输入框效果
- 【统计分析】3 空间点模型
- python turtle库详解_Python绘图Turtle库详解
- html设置编剧,编剧必备之电影剧本创作六大基本步骤
- RL论文阅读【一】Playing Atari with Deep Reinforcement Learning
- 【万里征程——Windows App开发】SemanticZoom视图切换
- 终于可以不用radmin了
- 怎么理解无界队列和有界队列
- 黑苹果在线安装版本安装教程
- uniapp从企业微信、微信公众号获取地址栏的参数
- Unity优化技巧,此时无光胜有光
- 秘密共享方案介绍SS