react中如何正确使用setState(附例子)
概述
setState中对于某个state多次修改,只执行一次(最后一次),所以可以将修改放在同一次中
import React, {Component} from 'react';
class Demotest extends Component {
constructor(props) {
super(props);
this.state = {
number: 1
};
}
componentDidMount() {
this.setState({ number: this.state.number + 1 });
}
addNumber(e) {
this.setState({ number: this.state.number + 1 });
this.setState({ number: this.state.number + 1 });
this.setState({ number: this.state.number + 1 });
console.log(this.state.number);
}
render() {
return (
<div>
<div>
<span>当前数字是{this.state.number}</span>
</div>
<br/>
<div>
<button onClick={e => {
this.addNumber(e);
}}>点击添加
</button>
</div>
</div>
);
}
}
export default Demotest;
初始加载后

这时发现页面上显示的是2,控制台输出的却是1,按道理 componentDidMount 里的应该已经成功了,不然不会显示2,那为什么控制台输出的却是1 呢?
由于 setState 是异步的所以,所以同步代码执行结束后才会执行,所以在 console.log('componentDidMount: ', this.state.number); 执行的时候 state 还没有被改变,所以生命周期里的输出还是原来的值。
此时我们点击按钮,触发函数 addNumber 发现,函数里的三次 setState 只生效了一次 ,页面显示的数字变成了3,控制台输出了**2 **(对应上面代码20行),这是因为多次更新被合并,而异步的原因导致只输出了2。官方文档上明确说明,如果希望通过这里的状态更新一下个状态,需要在 setState 中使用函数来取得

addNumber(e) {
this.setState((nextState) => {
console.log(nextState.number);
return { number: nextState.number + 1 };
});
this.setState((nextState) => {
console.log(nextState.number);
return { number: nextState.number + 1 };
});
this.setState((nextState) => {
console.log(nextState.number);
return { number: nextState.number + 1 };
});
}
此时输出的是2、3、4
这里要说明的是当你在 ****willMount** 前设进行 state 的设置不会 render 的触发,而事件和可以 componentDidMount 触发 render , 而且原生的事件可以优先这个机制
componentDidMount() {
document.body.addEventListener('click', this.updateData, false);
}
updateData = () => {
this.setState({
number: this.state.number + 1
});
console.log('componentDidMount: ', this.state.number);
};
详解setState
上面概述了下setState会出现的'合并',下面引用官网的一段话
setState()将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式
将setState()视为_请求_而不是立即更新组件的命令。为了更好的感知性能,React 会延迟调用它,然后通过一次传递更新多个组件。React 并不会保证 state 的变更会立即生效。
setState()并不总是立即更新组件。它会批量推迟更新。这使得在调用setState()后立即读取this.state成为了隐患。为了消除隐患,请使用componentDidUpdate或者setState的回调函数(setState(updater, callback)),这两种方式都可以保证在应用更新后触发。如需基于之前的 state 来设置当前的 state,请阅读下述关于参数updater的内容。
除非shouldComponentUpdate()返回false,否则setState()将始终执行重新渲染操作。如果可变对象被使用,且无法在shouldComponentUpdate()中实现条件渲染,那么仅在新旧状态不一时调用setState()可以避免不必要的重新渲染
下面通过几个例子分析setState在实际应用中的运用和注意点
附上一段源码
Component.prototype.setState = function(partialState, callback) {
// 校验是否符合三种情况
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
// 接受状态变量的对象以进行更新或者通过函数返回状态变量的对象
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
enqueueSetState: function (publicInstance, partialState) {
if (process.env.NODE_ENV !== 'production') {
ReactInstrumentation.debugTool.onSetState();
process.env.NODE_ENV !== 'production' ? warning(partialState != null, 'setState(...): You passed an undefined or null state object; ' + 'instead, use forceUpdate().') : void 0;
}
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
if (!internalInstance) {
return;
}
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
queue.push(partialState);
enqueueUpdate(internalInstance);
}
function enqueueUpdate(internalInstance) {
ReactUpdates.enqueueUpdate(internalInstance);
}
function enqueueUpdate(component) {
ensureInjected();
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case. (This is called by each top-level update
// function, like setState, forceUpdate, etc.; creation and
// destruction of top-level components is guarded in ReactMount.)
// 是批处理更新, 默认为false
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
dirtyComponents.push(component);
if (component._updateBatchNumber == null) {
component._updateBatchNumber = updateBatchNumber + 1;
}
}
setState的第一个参数
// 官网的api ---> 第一个参数可以是对象或者一个函数
setState(updater, [callback])
如果传入的是对象则会浅层合并到新的 state 中,后调用的 setState() 将覆盖同一周期内先调用 setState 的值
Object.assign(
previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)
相当于同个属性被合并。
如果第一个参数是函数,形式如下
(state, props) => stateChange
函数中接收的 state 和 props 都保证为最新, 是你的上次setState的状态值。
具体例子
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props)
this.state = {
number: 0
}
}
// 运行一遍,不理解的对照注释看
add () {
// 进入队列 number: 0 + 222 = 222
this.setState({
number: this.state.number + 222
})
// 进入队列 number: 0 + 5 =5
this.setState({
number: this.state.number + 5
})
// 进入队列 number: 5 + 1 = 6
this.setState((state, props) => {
// 此时state是上次setState中的number ==> 5
console.log('one', state);
return {
number: state.number + 1
}
})
// 进入队列 number: 0 + 1 = 1
this.setState({
number: this.state.number + 1
})
// 进入队列: 1 + 2 = 3
this.setState((state, props) => {
// 此时state是上次setState中的number ==> 1
console.log('two', state);
return {
number: state.number + 2
}
})
// 进入队列: 3 + 1 = 4
this.setState((state, props) => {
// 此时state是上次setState中的number ==> 3
console.log('three', state);
return {
number: state.number + 1
}
})
}
render () {
return (
<div>
<button onClick={e =>{this.add()}}>add</button>
{this.state.number}
</div>
);
}
}
export default App;
输出分别为 5 、 1 、 3,最后页面显示的是4。
第二个参数
setState()的第二个参数为可选的回调函数,它将在setState完成合并并重新渲染组件后执行。通常,我们建议使用componentDidUpdate()来代替此方式。
考虑如下场景:
在同个组件中一个具有进度条的页面需要不停的加载请求,只有当某次请求正确返回数据后出现新的内容区域。当数据返回后你将进度条比例设置为100,同时设置数据到state上。
this.ajax() {
this.$post(xxx.com)
.then(res => {
this.setState({
progress: 100,
data: res
})
})
.catch(err => {console.log(err)})
}
设置为100%是为了数据到来后进度条立马拉满,然后在渲染对应的数据内容区域,这样写会出现进度条其实也拉满了,但是视觉效果没出来数据内容区域就出来的情况。所以用到第二个参数
this.ajax() {
this.$post(xxx.com)
.then(res => {
this.setState({
progress: 100,
}, () => {
this.setState({
data: res
})
})
})
.catch(err => {console.log(err)})
}
react中如何正确使用setState(附例子)的更多相关文章
- (文章也有问题,请自行跳过)react中的状态机每次setState都是重新创建新的对象,如需取值,应该在render中处理。
demo如下 class Demo4StateLearn extends React.Component { constructor(props) { super(props); this.state ...
- PHP中递归的实现(附例子)
递归函数是一种调用自己的函数.写递归函数时要小心,因为可能会无穷递归下去.必须确保有充分的方法来终止递归. 一:使用参数引用完成递归函数.操作的是同一块内存地址. <?php $i=1; fun ...
- react中的setState的使用和深入理解
前端框架从MVC过渡到MVVM.从DOM操作到数据驱动,一直在不断的进步着,提升着, angular中用的是watcher对象,vue是观察者模式,react就是state了,他们各有各的特点,没有好 ...
- React中setState方法说明
setState跟新数据是同步还是异步? setState跟新数据是异步的. 如何用代码表现出来是异步的. 点击按钮更新数据,然后去打印这个值看一下 setState跟新数据是异步的 class Fa ...
- React 中的this.setState
在react中如何修改state中的数据 第一种写法:this.setState() 参数1:对象 需要修改的数据 参数2:回调 this.setState是一 ...
- React中this.setState是同步还是异步?为什么要设计成异步?
在使用react的时候,this.setState为什么是异步呢? 一直以来没有深思这个问题.昨天就此问题搜索了一下. react创始人之一 Dan Abramovgaearon在GitHub上回答了 ...
- React中setState学习总结
react中setState方法到底是异步还是同步,其实这个是分在什么条件下是异步或者同步. 1.先来回顾一下react组件中改变state的几种方式: import React, { Compone ...
- React中的setState到底发生了什么?
https://yq.aliyun.com/ziliao/301671 https://segmentfault.com/a/1190000014498196 https://blog.csdn.ne ...
- React中setState如何修改深层对象?
在React中经常会使用到setState,因为在react生态中,state就是一切.在开发过程中,时长会在state中遇到一些比较复杂的数据结构,类似下面这样的: 这时需要我们修改list中obj ...
- React中setState的怪异行为 ——setState没有即时生效
setState可以说是React中使用频率最高的一个函数了,我们都知道,React是通过管理状态来实现对组件的管理的,当this.setState()被调用的时候,React会重新调用render方 ...
随机推荐
- java.io.StreamCorruptedException: invalid stream header: 00013174
java.io.StreamCorruptedException: invalid stream header: 00013174 at java.io.ObjectInputStream.readS ...
- js中常用Math对象
js中常用Math对象 Math.round()四舍五入 Math.ceil() ->ceil 天花板--->向上取整 Math.floor() 向下取整 Math.floor(-11.1 ...
- pytest框架增加log打印(包括pytest的执行结果、自定义的log信息)
前言: 之前同事在跑脚本的时候报错了,发现没法把需要的log信息打印出来,我这边定位问题比较麻烦,所以增加了log的打印.这个问题之前已经处理过了,本来以为是小问题,结果今天又要用到这个小技巧的时候居 ...
- k8s-master 重启之后报错
The connection to the server 192.168.1.30:6443 was refused - did you specify the right host or port? ...
- TP3.2 I('post.') 获取到的参数特殊字符被自动转义
eg:原数据 :&这是一条测试数据 被转义后的数据:&nbmp; 这是一条测试数据 解决办法: $input = json_encode(I('post.'));$input = h ...
- DEV 导出多行头
//在winfrom 中添加个Gridcontrol 和按钮 public partial class MultTitle : XtraForm { BandedGridView bandedGrid ...
- Python学习—计算机与操作系统简介
计算机与操作系统简介 一.操作系统的主要发展史 1.手工操作--卡片穿孔 1946年第一台计算机诞生--20世纪50年代中期,计算机工作还在采用手工操作方式.此时还没有操作系统的概念.程序员将对应于程 ...
- js-防抖(简易版)
/** * 节流函数 */ var count = 1; var container = document.getElementById('container'); function getUse ...
- 【Java】@Scheduled注解各参数详解
每隔5秒执行一次:*/5 * * * * ? 每隔1分钟执行一次:0 */1 * * * ? 每天23点执行一次:0 0 23 * * ? 每天凌晨1点执行一次:0 0 1 * * ? 每月1号凌晨1 ...
- PyQt5模块说明
pyqt5的类别分为几个模块,包括以下: QtCoreQtGuiQtWidgetsQtMultimediaQtBluetoothQtNetworkQtPositioningEnginioQtWebSo ...