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方 ...
随机推荐
- 金蝶AAS-V9精简版使用手册1.6
1.安装 获取中间件安装包(AAAS.V9.ZIP)直接解压即可. 1.1.需安装jdk环境,上篇文章 1.2.路径没有特定要求,通常放置在/opt下 2.初次启动金蝶需要前台启动设置管控密码 lin ...
- C++用递归实现求解相关函数
//递归实现Hanoi塔问题#include<iostream>#include<cstdlib>using namespace std;#define MAXSIZE 100 ...
- 通过url跳转到页面锚点
在需要跳到的页面加: function GetQueryString(name) { var reg = new RegExp("(^|&)" + name ...
- Codeforces 1228A、Distinct Digits
原题 原题网址 题目大意 给定一个区间(上下界都是整数),判断该区间内是否存在一个整数的数字两两不同. 数据结构 一个数组flag,记录0-9是否出现过. 思路 外层循环枚举该区间内所有整数. 首先初 ...
- LP1-5:WEB应用测试技巧
一.静态页面 静态文字 1) 检查一个页面或者一组中多个页面的字体.size.颜色.位置等因素是否符合需求: 2)检查页面文字图标的间距.行距是否统一,对齐方式是否统一: 3)静态文字的含义是否符号需 ...
- 如果还有问ARKIME不会部署安装,你就把这篇丢给他!
前言 关于在线安装和离线安装的配合方式,在本文中不再赘述.基本就是准备在线.离线两个一模一样的环境,在在线环境中边安装边借助yumdownloader.wget等解析依赖和下载安装包,然后再离线环境中 ...
- 《黑马旅游网》综合案例六 BaseServlet 抽取
BaseServlet抽取 减少Servlet的数量,现在是一个功能一个Servlet,将其优化为一个模块一个Servlet, 相当于在数据库中一张表对应一个Servlet,在Servlet中提供不同 ...
- JavaSE——.replace()方法替换内容
package com.zhao.stringtest; public class Test5 { //.replace()方法替换词汇 public static void main(String[ ...
- Win10系统将bat文件注册成服务
代码语法: sc create ServiceName binPath= 路径 start= auto 示例语句: sc create Tomcat binPath= F:/tomcat/bin/st ...
- Python爬虫抓取图片(re模块处理正则表达式)
import os.path import re import requests if __name__ == '__main__': # 如果不存在该文件夹则进行创建 if not os.path. ...