概述

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(附例子)的更多相关文章

  1. (文章也有问题,请自行跳过)react中的状态机每次setState都是重新创建新的对象,如需取值,应该在render中处理。

    demo如下 class Demo4StateLearn extends React.Component { constructor(props) { super(props); this.state ...

  2. PHP中递归的实现(附例子)

    递归函数是一种调用自己的函数.写递归函数时要小心,因为可能会无穷递归下去.必须确保有充分的方法来终止递归. 一:使用参数引用完成递归函数.操作的是同一块内存地址. <?php $i=1; fun ...

  3. react中的setState的使用和深入理解

    前端框架从MVC过渡到MVVM.从DOM操作到数据驱动,一直在不断的进步着,提升着, angular中用的是watcher对象,vue是观察者模式,react就是state了,他们各有各的特点,没有好 ...

  4. React中setState方法说明

    setState跟新数据是同步还是异步? setState跟新数据是异步的. 如何用代码表现出来是异步的. 点击按钮更新数据,然后去打印这个值看一下 setState跟新数据是异步的 class Fa ...

  5. React 中的this.setState

    在react中如何修改state中的数据     第一种写法:this.setState()         参数1:对象 需要修改的数据         参数2:回调 this.setState是一 ...

  6. React中this.setState是同步还是异步?为什么要设计成异步?

    在使用react的时候,this.setState为什么是异步呢? 一直以来没有深思这个问题.昨天就此问题搜索了一下. react创始人之一 Dan Abramovgaearon在GitHub上回答了 ...

  7. React中setState学习总结

    react中setState方法到底是异步还是同步,其实这个是分在什么条件下是异步或者同步. 1.先来回顾一下react组件中改变state的几种方式: import React, { Compone ...

  8. React中的setState到底发生了什么?

    https://yq.aliyun.com/ziliao/301671 https://segmentfault.com/a/1190000014498196 https://blog.csdn.ne ...

  9. React中setState如何修改深层对象?

    在React中经常会使用到setState,因为在react生态中,state就是一切.在开发过程中,时长会在state中遇到一些比较复杂的数据结构,类似下面这样的: 这时需要我们修改list中obj ...

  10. React中setState的怪异行为 ——setState没有即时生效

    setState可以说是React中使用频率最高的一个函数了,我们都知道,React是通过管理状态来实现对组件的管理的,当this.setState()被调用的时候,React会重新调用render方 ...

随机推荐

  1. classmethod 修饰符

    title: Python classmethod 修饰符 author: 杨晓东 permalink: classmethod date: 2021-10-02 11:27:04 categorie ...

  2. mysql查询最近2天数据

    SELECT * FROM jk_dzbl_zybrbljlb where jlrq >= now()-interval 2 day

  3. jmeter进行websocket 通信

    最近项目弄了基于websocket的通信接口,所以需要做一个websocket的接口压测. jmeter当前自带的插件无法进行jmeter接口的通信,所以 本人是下载的一套jar包插件,进行的安装 1 ...

  4. Django的反向解析

    Django的请求生命周期是指用户在浏览器访问网页时,Django根据网址在路由列表里查找相应的路由,在从路由里找到视图函数或视图类进行处理,将处理结果作为相应内容返回浏览器并生成网页内容. 这个生命 ...

  5. Docker上安装MSSQL(SQL Server)

    ​ Mac OS X ,想安装微软的mssql-server数据库有三种方式: 第一种是在本机上安装MSSQL for Linux 版本. 第二种是安装Windows虚拟机,然后在虚拟机里面使用ISO ...

  6. excel快速生成相同内容

    先选中你所需要的单元格,如图所示: 输入自己想要的字符,然后按ctrl+enter,如图所示:

  7. wireshark作业

    1Wireshark基本操作: 1.启动wireshark,正确选择混杂模式,访问任意网站: 2.设置过滤器呈现本地和该网站服务器之间的交互报文: 3.保存抓包结果文件.cap: 4.在作业纸上记录下 ...

  8. 接入GoogelAdmob C#服务器端验证 (SSV) 回调

    记一次接入谷歌广告验证 官方文档:https://developers.google.cn/admob/android/rewarded-video-ssv 需要httpserver进行验证,项目里有 ...

  9. Python的入门学习Day 28~30——form”夜曲编程“

    Day 28 in Day 29 time: 2021.8.26. ​ 时间模糊了界限,虽我日渐走远.转眼而过的二十多天,既留下了夏天在沙滩上的足迹,同时也为黄金色的秋日铺上留白的画卷.键盘敲时,熟悉 ...

  10. 14.java 中缀表达式转后缀表达式

    思路如下: 1.初始化两个栈,运算符栈和中间结果栈 2.从左至右扫描 3.遇到数时直接压入s2 4.遇到运算符时,比较其与s1栈顶的优先级,有如下几种情况: 1)s1为空或栈顶为"(&quo ...