加入收藏 | 设为首页 | 会员中心 | 我要投稿 我爱故事小小网_铜陵站长网 (http://www.0562zz.com/)- 视频终端、云渲染、应用安全、数据安全、安全管理!
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

react的setstate是同步或是异步?

发布时间:2022-01-11 14:20:53 所属栏目:语言 来源:互联网
导读:这篇文章主要给大家分享的是关于react的setstate的内容,对于setstate是同步还是异步的问题,一些朋友可能不是理解,对此我们通过示例来了解一下,感兴趣的朋友就继续往下看吧。 setState是同步还是异步 自定义合成事件和react钩子函数中异步更新state 以
  这篇文章主要给大家分享的是关于react的setstate的内容,对于setstate是同步还是异步的问题,一些朋友可能不是理解,对此我们通过示例来了解一下,感兴趣的朋友就继续往下看吧。
 
  setState是同步还是异步
  自定义合成事件和react钩子函数中异步更新state
  以在自定义click事件中的setState为例
 
  import React, { Component } from 'react';
  class Test extends Component {
    constructor(props) {
      super(props);
      this.state = {
        count: 1
      };
    }
    handleClick = () => {
      this.setState({
        count: this.state.count + 1
      });
      this.setState({
        count: this.state.count + 1
      });
      this.setState({
        count: this.state.count + 1
      });
      console.log(this.state.count);
    }
    render() {
      return (
        <div style={{ width: '100px', height: '100px', backgroundColor: "yellow" }}>
            {this.state.count}
        </div>
      )
    }
  }
  export default Test;
  点击一次,最终this.state.count的打印结果是1,页面展示的是2。通过现象看,三次setState只是最后一次setState生效了,前两次都setState无效果。因为假如把第一次setState改为+3,count打印结果为1,展示结果为2,没有发生变化。而且没有同步获得count的结果。
 
  此时,我们可以调整代码,通过setState的第二个参数,来获得更新后的state:
 
  import React, { Component } from 'react';
  class Test extends Component {
    constructor(props) {
      super(props);
      this.state = {
        count: 1
      };
    }
    handleClick = () => {
      this.setState({
        count: this.state.count + 3
      }, () => {
        console.log('1', this.state.count)
      });
      this.setState({
        count: this.state.count + 1
      }, () => {
        console.log('2', this.state.count);
      });
      this.setState({
        count: this.state.count + 1
      }, () => {
        console.log('3', this.state.count);
      });
      console.log(this.state.count);
    }
    render() {
      return (
        <div style={{ width: '100px', height: '100px', backgroundColor: "yellow" }}>
            {this.state.count}
        </div>
      )
    }
  }
  export default Test;
  此时,点击一次,三个setState的回调函数中,打印结果分别是。
 
  1
  1: 2
  2: 2
  3: 2
 
  首先,最后一行直接打印1。然后,在setState的回调中,打印出的结果都是最新更新的2。虽然前两次setState未生效,但是它们第二个参数中还是会打印出2。
 
  此时将setState的第一个参数换成函数,通过函数的第一个参数可以获得更新前的state。
 
  import React, { Component } from 'react';
  class Test extends Component {
    constructor(props) {
      super(props);
      this.state = {
        count: 1
      };
    }
    handleClick = () => {
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      });
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      });
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      });
      console.log(this.state.count);
    }
    render() {
      return (
        <div style={{ width: '100px', height: '100px', backgroundColor: "yellow" }}>
            {this.state.count}
        </div>
      )
    }
  }
  export default Test;
  此时,打印出的结果为1,但是页面展示出来的count为4。可以发现,如果setState以传参的方式去更新state,几次setState并不会只更新最后一次,而是几次更新state都会生效。
 
  接下来看下第二个函数中打印的count是多少:
 
  import React, { Component } from 'react';
  class Test extends Component {
    constructor(props) {
      super(props);
      this.state = {
        count: 1
      };
    }
    handleClick = () => {
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      }, () => {
        console.log('1', this.state.count);
      });
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      }, () => {
        console.log('2', this.state.count);
      });
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      }, () => {
        console.log('3', this.state.count);
      });
      console.log(this.state.count);
    }
    render() {
      return (
        <div style={{ width: '100px', height: '100px', backgroundColor: "yellow" }}>
            {this.state.count}
        </div>
      )
    }
  }
  export default Test;
  此时,点击一次,三个setState的回调函数中,打印结果如下,可想而知,页面的展示结果也为4
 
  1
  1: 4
  2: 4
  3: 4
 
  将上边代码放入如componentDidMount中,输出结果跟上边一致。
 
  因为,可以得知,在自定义合成事件和钩子函数中,state的更新是异步的。
 
  原生事件和setTimeout中同步更新state
  以在setTimeout中setState为例
 
  import React, { Component } from 'react';
  class Test extends Component {
    constructor(props) {
      super(props);
      this.state = {
        count: 1
      };
    }
    componentDidMount() {
      setTimeout(() => {
        this.setState({
          count: this.state.count + 1
        }, () => {
          console.log('1:', this.state.count);
        });
        this.setState({
          count: this.state.count + 1
        }, () => {
          console.log('2:', this.state.count);
        });
        this.setState({
          count: this.state.count + 1
        }, () => {
          console.log('3:', this.state.count);
        });
        console.log(this.state.count);
      }, 0);
    }
    render() {
      return (
        <div
          style={{
            width: '100px',
            height: '100px',
            backgroundColor: "yellow"
          }}>
            {this.state.count}
        </div>
      )
    }
  }
  export default Test;
  此时,打印出的结果如下:
 
  1: 2
  2: 3
  3: 4
  4
 
  将setState第一个参数换为函数:
 
  componentDidMount() {
    setTimeout(() => {
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      }, () => {
        console.log('1', this.state.count);
      });
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      }, () => {
        console.log('2', this.state.count);
      });
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      }, () => {
        console.log('3', this.state.count);
      });
      console.log(this.state.count);
    }, 0);
  }
  打印出的结果和上边一致。
 
  是不是有一种state完全可控的感觉,在setTimeout中,多次setState都会生效,而且在每一个setState的第二个参数中都可以得到更新后的state。
 
  同样地,在原生事件中输出地结果和setTimeout中一致,也是同步的。
 
  import React, { Component } from 'react';
  class Test extends Component {
    constructor(props) {
      super(props);
      this.state = {
        count: 1
      };
    }
    componentDidMount() {
      document.body.addEventListener('click', this.handleClick, false);
    }
    componentWillUnmount() {
      document.body.removeEventListener('click', this.handleClick, false);
    }
    handleClick = () => {
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      }, () => {
        console.log('1', this.state.count);
      });
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      }, () => {
        console.log('2', this.state.count);
      });
      this.setState((prevState, props) => {
        return { count: prevState.count + 1 }
      }, () => {
        console.log('3', this.state.count);
      });
      console.log(this.state.count);
    }
    render() {
      return (
        <div
          style={{
            width: '100px',
            height: '100px',
            backgroundColor: "yellow"
          }}
        >
          {this.state.count}
        </div>
      )
    }
  }
  export default Test;
  setState相关源码
  如下代码均来自react17.0.2版本
 
  目录 ./packages/react/src/ReactBaseClasses.js
 
  function Component(props, context, updater) {
    this.props = props;
    this.context = context;
    // If a component has string refs, we will assign a different object later.
    this.refs = emptyObject;
    // We initialize the default updater but the real one gets injected by the
    // renderer.
    this.updater = updater || ReactNoopUpdateQueue;
  }
 
  Component.prototype.isReactComponent = {};
 
  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');
  };
  setState可以接收两个参数,第一个参数可以是object,function,和null,undefined,就不会抛出错误。执行下边的this.updater.enqueueSetState方法。全局查找enqueueSetState,找到两组目录下有这个变量。
 
  首先是第一组目录:
 
  目录 ./packages/react/src/ReactNoopUpdateQueue.js 第100行enqueueSetState方法,参数分别为this,初始化state,回调,和字符串setState,this是指当前React实例。
 
  enqueueSetState: function(
    publicInstance,
    partialState,
    callback,
    callerName,
  ) {
    warnNoop(publicInstance, 'setState');
  }
  接着看warnNoop方法:
 
  const didWarnStateUpdateForUnmountedComponent = {};
 
  function warnNoop(publicInstance, callerName) {
    if (__DEV__) {
      const constructor = publicInstance.constructor;
      const componentName =
        (constructor && (constructor.displayName || constructor.name)) ||
        'ReactClass';
      const warningKey = `${componentName}.${callerName}`;
      if (didWarnStateUpdateForUnmountedComponent[warningKey]) {
        return;
      }
      console.error(
        "Can't call %s on a component that is not yet mounted. " +
          'This is a no-op, but it might indicate a bug in your application. ' +
          'Instead, assign to `this.state` directly or define a `state = {};` ' +
          'class property with the desired state in the %s component.',
        callerName,
        componentName,
      );
      didWarnStateUpdateForUnmountedComponent[warningKey] = true;
    }
  }
  这段代码相当于给didWarnStateUpdateForUnmountedComponent对象中加入属性,属性的key为React 当前要setState的组件.setState,如果当前有这个属性则返回;如果当前没这个属性或者这个属性值为false,则设置这个属性的值为true。
 
  再去看另外一个目录:
 
  目录 ./react-reconciler/src/ReactFiberClassComponent.new.js和ReactFiberClassComponent.old.js
 
  const classComponentUpdater = {
    enqueueSetState(inst, payload, callback) {
      const fiber = getInstance(inst);
      const eventTime = requestEventTime();
      const lane = requestUpdateLane(fiber);
 
      const update = createUpdate(eventTime, lane);
      update.payload = payload;
      if (callback !== undefined && callback !== null) {
        if (__DEV__) {
          warnOnInvalidCallback(callback, 'setState');
        }
        update.callback = callback;
      }
 
      enqueueUpdate(fiber, update, lane);
      const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
      if (root !== null) {
        entangleTransitions(root, fiber, lane);
      }

(编辑:我爱故事小小网_铜陵站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读