概述

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. 【C学习笔记】day4-1 在屏幕上输出以下图案

    1.在屏幕上输出以下图案: * *** ***** ******* ********* *********** ************* *********** ********* ******* ...

  2. 实现两个APP之间的跳转传值

    应用A 跳转到 应用B 1.在B中设置URL Schemes     加入一项item 并赋值,比如kiloMeter 并在B中实现 - (BOOL)application:(UIApplicatio ...

  3. leecode75. 颜色分类

    75. 颜色分类 给定一个包含红色.白色和蓝色.共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色.白色.蓝色顺序排列. 我们使用整数 0. 1 和 2 分别表示 ...

  4. 神奇的Object.assign()

    Object.assign() 方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象.它将返回目标对象. 1.Object.assign()可以在对象为一层的时候,实现简单的"深拷 ...

  5. 升级grafana

    We recommend everyone to upgrade Grafana often to stay up to date with the latest fixes and enhancem ...

  6. 2021-12-14 MobX分享

    MobX分享 文档地址: https://cn.mobx.js.org/ MobX是一种简单的.可扩展的状态管理,它通过透明的函数响应式编程使得状态管理变得简单和可扩展. React通过提供机制把应用 ...

  7. VS Code编写stm32

    说明 virtual code实现编辑功能,其有强大的代码提示.代码阅读功能. 通过bat文件,通过cmd编译.下载,具体下载器设置仍需在keil软件中设置 具体代码调试仍需在keil软件下 项目配置 ...

  8. docker-compose任务编排

    一.docker-compose介绍 使用一个Dockerfile模板文件可以定义一个单独的应用容器,如果需要定义多个容器就需要服务编排.下面介绍Docker官方产品,Docker Compose. ...

  9. 常见的Native Crash类型,bug解决记录

    APP调用Native的jar包接口出现闪退,仅仅settings应用,其他应用调用该包接口正常使用. 猜测1. jar包为64为,settings程序为32位,版本兼容性问题.(经验证,原因确实如此 ...

  10. 记录一次antd升级到最新版本,与现有代码冲突导致的问题

    背景:发版的前一夜,测试突然发现项目某个功能点击弹框会导致整个页面直接空白,立即提了个单要我赶紧修复.(内心真是一万个卧槽)本来准备不加班的.没办法,那只能解决.第一步就怀疑是不是谁动了代码,毕竟一两 ...