广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >详解react setState
  • 538
分享到

详解react setState

2024-04-02 19:04:59 538人浏览 薄情痞子
摘要

目录setState是同步还是异步 自定义合成事件和React钩子函数中异步更新state 原生事件和setTimeout中同步更新state setState相关源码 总结 set

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);
    }

    if (__DEV__) {
      if (enableDebugTracing) {
        if (fiber.mode & DebugTracingMode) {
          const name = getComponentNameFromFiber(fiber) || 'Unknown';
          logStateUpdateScheduled(name, lane, payload);
        }
      }
    }

    if (enableSchedulingProfiler) {
      markStateUpdateScheduled(fiber, lane);
    }
  }
}

其中主要看 enqueueUpdate 这个函数

目录 ./react-reconciler/src/ReactUpdateQueue.new.js和ReactUpdateQueue.old.js


export function enqueueUpdate<State>(
  fiber: Fiber,
  update: Update<State>,
  lane: Lane,
) {
  const updateQueue = fiber.updateQueue;
  if (updateQueue === null) {
    // Only occurs if the fiber has been unmounted.
    return;
  }

  const sharedQueue: SharedQueue<State> = (updateQueue: any).shared;

  if (isInterleavedUpdate(fiber, lane)) {
    const interleaved = sharedQueue.interleaved;
    if (interleaved === null) {
      // This is the first update. Create a circular list.
      update.next = update;
      // At the end of the current render, this queue's interleaved updates will
      // be transfered to the pending queue.
      pushInterleavedQueue(sharedQueue);
    } else {
      update.next = interleaved.next;
      interleaved.next = update;
    }
    sharedQueue.interleaved = update;
  } else {
    const pending = sharedQueue.pending;
    if (pending === null) {
      // This is the first update. Create a circular list.
      update.next = update;
    } else {
      update.next = pending.next;
      pending.next = update;
    }
    sharedQueue.pending = update;
  }

  if (__DEV__) {
    if (
      currentlyProcessingQueue === sharedQueue &&
      !didWarnUpdateInsideUpdate
    ) {
      console.error(
        'An update (setState, replaceState, or forceUpdate) was scheduled ' +
          'from inside an update function. Update functions should be pure, ' +
          'with zero side-effects. Consider using componentDidUpdate or a ' +
          'callback.',
      );
      didWarnUpdateInsideUpdate = true;
    }
  }
}

看到这里,发现这个方法是将此次更新的update加入到更新队列中,而在这个版本中并没有发现isBatchingUpdates这个属性的出现。貌似React Fiber改动还挺大,暂时先写到这里,如果有新的发现会补充到这里。

总结

  • 自定义合成事件和react钩子函数中异步更新state
  • 原生事件和setTimeout中同步更新state

以上就是详解react setState的详细内容,更多关于react setState的资料请关注编程网其它相关文章!

--结束END--

本文标题: 详解react setState

本文链接: https://www.lsjlt.com/news/123272.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解react setState
    目录setState是同步还是异步 自定义合成事件和react钩子函数中异步更新state 原生事件和setTimeout中同步更新state setState相关源码 总结 set...
    99+
    2022-11-12
  • 详解React setState数据更新机制
    目录为什么使用setState setState 的用法 异步更新还是同步更新 总结 为什么使用setState 在React 的开发过程中,难免会与组件的state打交道。使用过...
    99+
    2022-11-12
  • React使用useEffect解决setState副作用详解
    目录介绍一下APIfetch()方法访问APIsetState的副作用使用useEffect解决这个问题使用useEffect操控函数运行介绍一下API 本文主要内容:描述了setS...
    99+
    2022-11-13
    React useEffect解决setState React useEffect setState
  • React 中 setState 的异步操作案例详解
    目录前言React 中的 setState 为什么需要异步操作?什么时候setState会进行同步操作?前言 在使用state的时候, 如果我们企图直接...
    99+
    2022-11-13
  • React中setState使用原理解析
    目录setState使用详解使用setState的原因setState的基本用法setState的异步更新setState获取异步结果setState一定是异步setState使用详...
    99+
    2022-11-13
    React setState使用 React setState
  • React中setState/useState的使用方法详细介绍
    目录一、React如何使用setState/useState的最新的值A.使用setState的最新的值B.使用useState的最新的值二、React中setState/useSt...
    99+
    2023-05-14
    React setState与useState React setState React useState
  • React如何使用useEffect解决setState副作用
    本篇内容主要讲解“React如何使用useEffect解决setState副作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“React如何使用useEffect解决setState副作用”吧!...
    99+
    2023-07-04
  • setState如何在react 中使用
    setState如何在react 中使用?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。setState是同步还是异步自定义合成事件和react钩子函数中异步更新state以...
    99+
    2023-06-14
  • react调用setstate实例分析
    这篇文章主要介绍“react调用setstate实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“react调用setstate实例分析”文章能帮助大家解决问题...
    99+
    2022-10-19
  • React Context与setState如何使用
    这篇文章主要介绍“React Context与setState如何使用”,在日常操作中,相信很多人在React Context与setState如何使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对...
    99+
    2023-07-04
  • react源码层探究setState作用
    目录前言为什么setState看起来是异步的从first paint开始触发组件更新更新渲染fiber tree写在最后前言 在深究 React 的 setState 原理的时候,我...
    99+
    2022-11-13
    react setState react setState源码
  • React setState异步原理是什么
    本文小编为大家详细介绍“React setState异步原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“React setState异步原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习...
    99+
    2023-07-04
  • react中state以及setState怎么用
    这篇文章主要介绍react中state以及setState怎么用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!在react中通过 state 以及 setState() 来控制组件的...
    99+
    2022-10-19
  • react中setstate的概念是什么
    这篇文章主要介绍“react中setstate的概念是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“react中setstate的概念是什么”文章能帮助大家解决...
    99+
    2022-10-19
  • react的setstate什么时候同步
    本教程操作环境:Windows10系统、react18.0.0版、Dell G3电脑。react的setstate什么时候同步?什么时候是异步的? 先给出答案: 有时表现出异步,有时表现出同步。setState只在合成事件和钩子函数中是“异...
    99+
    2023-05-14
    setstate React
  • React中的setState/useState怎么使用
    这篇文章主要介绍“React中的setState/useState怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“React中的setState/useState怎么使用”文章能帮助大家解决问...
    99+
    2023-07-05
  • 代码解析React中setState同步和异步问题
     React起源于Facebook的内部项目。React的出现是革命性的创新,React的是一个颠覆式的前端框架。在React官方这样介绍的它:一个声明式、高效、灵活的、创...
    99+
    2022-11-12
  • React中setState源码的示例分析
    这篇文章将为大家详细讲解有关React中setState源码的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。React作为一门前端框架,虽然只是focus在MVV...
    99+
    2022-10-19
  • react的setstate同步情况是什么
    本文小编为大家详细介绍“react的setstate同步情况是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“react的setstate同步情况是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。react...
    99+
    2023-07-04
  • 关于React中setState同步或异步问题的理解
    目录1. setState同步?异步? 2. 表现为异步 1. React 合成事件 2. 生命周期函数 3. 表现为同步 1. 原生事件 2. setTimeout 4. setS...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作