关于React中的setState

智数游云
• 阅读 1204

在react中,setState是用以改变class组件状态的函数,它有两种用法:
一 传入一个updater函数,该函数有两个参数,一个是当前的state,还有一个是当前的props。该函数的返回值需要是一个更改的state值的对象,它将于state进行浅合并,其用法如下:

    this.setState((state, props) => {
        return { count: state.count + props.number };
    });

二 直接传入一个对象:

    this.setState({ count: this.state.count + this.props.number });

setState函数还可以接受第二个参数,该参数为一个函数,将在更改的State生效之后调用:

    console.log(this.state.count); // 1
    this.setState({ count: 0 }, () => {
        console.log(this.state.count); // 0
    });
    console.log(this.state.count); // ? 此处即可能是1,也可能是0

从上面代码可以看到,最后一行输出的count是不固定的,这是为什么呢?
因为在react中,class内的事件处理程序会默认进行批处理,即如果你在componentDidMount里面调用三次setState函数,那么它最终会在componentDidMount执行完毕后,将三个State的更改合并为一次调用。所以这时候setState就是异步的。
而在其他场景下,setState将会是同步的,例如setTimeout内, Promise的then里面。
一个简单的例子:

class SetStateExample extends Component {
  constructor() {
    super();
    this.state = {
      count: 0
    };

    this.onClick = this.onClick.bind(this);
  }

  componentDidMount() {
    console.log('componentDidMount before', this.state.count);
    this.setState({ count: this.state.count + 1 });
    console.log('componentDidMount after', this.state.count);
  }

  onClick() {
    console.log('onClick before', this.state.count)
    this.setState({ count: this.state.count + 1 }, () => {
      console.log('setState callback', this.state.count);
    });
    console.log('onClick after', this.state.count);
    Promise.resolve().then(() => {
      console.log('promise.then before', this.state.count);
      this.setState({ count: this.state.count + 1 });
      console.log('promise.then after', this.state.count);
      this.onClassEvent();
    });
  }

  onClassEvent() {
    console.log('onClassEvent before', this.state.count);
    this.setState({ count: this.state.count + 1 });
    console.log('onClassEvent after', this.state.count);
  }

  render() {
    return <div className="test">
      <div>count: {this.state.count}</div>
      <button onClick={this.onClick}>点击改变count</button>
    </div>;
  }
}

让我们运行结果:

关于React中的setState

首先第一第二行输出是在componentDidMount里面,我们在函数内调用了setState,并在前后分别输出了改变的值,结果表明,函数调用前与函数调用后该值并没有立即改变,则表明在这里setState是一个异步调用。那么初步判定在生命周期函数内部,setState是异步的调用。

然后第三第四行输出是在onClick函数的回调里面,该函数定义在class中,通过用户点击触发。在setState调用前后我们的输出结果是一致的,这也表明其是一个异步调用。而在setState第二个参数中我们输出了改变后的count, 即第五行输出,表明我们的更改生效了。

然后第六行以后的输出是我们在onClick函数内调用了promise.resolve().then()输出的,它是一个异步调用,react是无法知道它什么时候执行,什么时候完成执行的,所以这时候react默认setState是同步的。从输出我们可以看到每次更改之后,state的值都是立即变化生效的。

而在promise的回调内,我们还调用了一个定义于class内的事件函数,但是该事件函数内的setState也是同步的形式。这说明了setState的同步或者异步与其定义位置并没有直接的关系,而应该取决于是否由React直接进行调用,因为只有是React直接调用的情况下,它才知道该函数什么时候执行完毕,才能进行批处理的优化。否则则默认是同步的调用。(具体内部实现就不展开了,因为我也不是特别懂HHHH,反正意思就大概是这么个意思)

所以当在一些回调内部调用setState时应该注意将多个setState合并,因为它是同步的,多次更新状态会很影响性能。
以及需要注意进行异步调用的时候,如果需要使用变化后的值,请确保在异步调用完成后,一般是在setState的回调内,或者在componentDidUpdate钩子内,但是请注意小心使用,因为很容易一不小心导致循环调用而崩溃。

如果你想在本应同步调用的回调内,对setState进行异步调用,即让它进行批处理,React也提供了一个API:

promise.then(() => {
  // Forces batching
  ReactDOM.unstable_batchedUpdates(() => {
    this.setState({a: true}); // Doesn't re-render yet
    this.setState({b: true}); // Doesn't re-render yet
    this.props.setParentState(); // Doesn't re-render yet
  });
  // When we exit unstable_batchedUpdates, re-renders once
});

在unstable_batchedUpdates内部进行的setState会是异步调用,但是该API是不稳定的,因为后续的React版本更新中将会默认进行批处理即异步调用,届时该API将被删除。而这个后续的版本,很可能就是React 17

记录与分享,欢迎斧正,虚心求教

参考连接:
https://stackoverflow.com/que...
https://react.docschina.org/d...
https://github.com/Advanced-F...
https://github.com/sisterAn/b...

点赞
收藏
评论区
推荐文章
海军 海军
4年前
React Hook丨用好这9个钩子,所向披靡
ReactHook指南什么是Hook?Hook是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。Hook本质上就是一个函数,它简洁了组件,有自己的状态管理,生命周期管理,状态共享。useStateuseEffectuseContextus
React Hooks源码深度解析
ReactHooks是React16.8引入的一个新特性,它允许函数组件中使用state和其他React特性,而不必使用类组件。Hooks是一个非常重要的概念,因为它们提供了更简单、更易于理解的React开发体验。本篇文章以ReactHooks源码为基,进行深度解析复盘其实现原理。
爱丽丝13 爱丽丝13
4年前
React 组件通信之发布订阅模式
React通信react的数据流是单向的,react通信有以下几种方式:父向子通信:传入props子向父通信:父组件向子组件传一个函数,然后通过这个函数的回调,拿到子组件传过来的值父向孙通信:利用context传值。React.createContext()兄弟间通信:​1、找一个相同的父组件,既可以用pr
亚瑟 亚瑟
4年前
自定义 Hook – React
自定义Hook_Hook_是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。通过自定义Hook,可以将组件逻辑提取到可重用的函数中。在我们学习时,我们已经见过这个聊天程序中的组件,该组件用于显示好友的在线状态:importReact,{useSta
亚瑟 亚瑟
4年前
使用 Effect Hook – React
使用EffectHook_Hook_是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。_EffectHook_可以让你在函数组件中执行副作用操作importReact,{useState,useEffect}from'reac
亚瑟 亚瑟
4年前
Hook 规则 – React
Hook规则_Hook_是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的React特性。Hook本质就是JavaScript函数,但是在使用它时需要遵循两条规则。我们提供了一个来强制执行这些规则:只在最顶层使用Hook不要在循环,条件或嵌套函数中调用Hoo
Stella981 Stella981
3年前
React Hooks 之 useReducer useRef useContext和useMemo 的用法
之前文章中我们有讲到的useStatehook和useEffecthook的一些概念和基本用法,趁着周末空闲时间,我们一起来看看React为我们提供的其他hooks的一些基本使用吧。useReduceruseReducerhook接收两个参数,第一个是参数是一个函数(这是一个reducer函数:它接收两个
Stella981 Stella981
3年前
React.js 时间组件 + 组件生命周期(更新模拟)
React是用于构建用户界面的JavaScript库,React组件使用一个名为render()的方法,接收数据作为输入,输出页面中对应展示的内容。React除了可以使用外部传入的数据以外(通过this.props访问传入数据),组件还可以拥有其内部的状态数据(通过this.state访问状态数据)。当组件的状态
Stella981 Stella981
3年前
React对state的初级理解
props是参数,个个Class之间传值用的。state则为某个Class的内部状态,Class需要根据这个state是否改变而做出改变;<div id"content"</div    <script type"text/babel"      var CommentForm  React.createC
Stella981 Stella981
3年前
React(5)
绑定函数事件在以类继承的方式定义的组件中,为了能方便地调用当前组件的其他成员方法或属性(如:this.state),通常需要将事件处理函数运行时的this指向当前组件实例。run(){  alert('我是一个run方法')}<buttononClick{this.run}执行方法</button //方法
可莉 可莉
3年前
04. react 初次见面
    组件从概念上看就像是函数,它可以接收任意的输入值(称之为“props”),并返回一个需要在页面上展示的React元素。1、组件定义的两种方式1.1函数定义组件  定义一个组件最简单的方式是使用JavaScript函数:functionWelcome(props){return<
智数游云
智数游云
Lv1
故园肠断处,日夜柳条新。
文章
3
粉丝
0
获赞
0