广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >React中setState同步异步场景的使用
  • 451
分享到

React中setState同步异步场景的使用

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

目录setState同步异步场景描述原理保证内部数据统一启用并发更新参考setState同步异步场景 React通过this.state来访问state,通过this.setStat

setState同步异步场景

React通过this.state来访问state,通过this.setState()方法来更新state,当this.setState()方法被调用的时候,React会重新调用render方法来重新渲染UI。相比较于在使用Hooks完成组件下所需要的心智负担,setState就是在使用class完成组件下所需要的心智负担,当然所谓的心智负担也许叫做所必须的基础知识更加合适一些。

描述

setState只在合成事件和生命周期钩子函数中是异步的,而在原生事件中都是同步的,简单实现一个React Class TS例子。

import React from "react";
import "./styles.CSS";

interface Props{}
interface State{
  count1: number;
  count2: number;
  count3: number;
}
export default class App extends React.Component<Props, State>{
  constructor(props: Props){
    super(props);
    this.state = {
      count1: 0,
      count2: 0,
      count3: 0
    }
  }
  
  incrementAsync = () => {
    console.log("incrementAsync before setState", this.state.count1);
    this.setState({
      count1: this.state.count1 + 1
    });
    console.log("incrementAsync after setState", this.state.count1);
  }
  incrementSync = () => {
    setTimeout(() => {
      console.log("incrementSync before setState", this.state.count2);
      this.setState({
        count2: this.state.count2 + 1
      });
      console.log("incrementSync after setState", this.state.count2);
    },0);
  }
  incrementAsyncFn = () => {
    console.log("incrementAsyncFn before setState", this.state.count3);
    this.setState({
        count3: this.state.count3 + 1
        },
        () => {
            console.log("incrementAsyncFn after.1 setState", this.state.count3);
        }
    );
    this.setState(state => {
      console.log("incrementAsyncFn after.2 setState", state.count3);
      return {count3: state.count3}
    });
  }
  render(){
    return <div>
      <button onClick={this.incrementAsync}>异步</button>
      <button onClick={this.incrementSync}>同步</button>
      <button onClick={this.incrementAsyncFn}>异步函数参数</button>
    </div>
  }
}

以此点击三个按钮的话,可以得到以下输出。

incrementAsync before setState 0
incrementAsync after setState 0
incrementSync before setState 0
incrementSync after setState 1
incrementAsyncFn before setState 0
incrementAsyncFn after.2 setState 1
incrementAsyncFn after.1 setState 1

首先看incrementAsync的结果,在这里我们可以看出,在合成事件调用setState之后,this.state是无法立即得到最新的值。
对于incrementSync的结果,在非合成事件的调用时,this.state是可以立即得到最新的值的,例如使用addEventListenersetTimeoutsetInterval等。
对于incrementAsyncFn的两个结果,首先来说after.2的结果,对于this.state也是可以得到最新的值的,如果你需要基于当前的state来计算出新的值,那么就可以通过传递一个函数,而不是一个对象的方式来实现,因为setState的调用是分批的,所以通过传递函数可以链式地进行更新,当然前提是需要确保它们是一个建立在另一个之上的,也就是说传递函数的setState的值是依赖于上次一的SetState的,对于after.1的结果,setState函数的第二个参数是一个回调函数,在setState批量更新完成后可以拿到最新的值,而after.2也是属于批量更新需要调用的函数,所以after.1会在after.2后执行。

原理

React将其实现为异步的动机主要是性能的考量,setState的异步并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和生命周期钩子函数的调用顺序在批处理更新之前,导致在合成事件和生命周期钩子函数中没法立马拿到更新后的值,形式了所谓的异步,实际上是否进行批处理是由其内部的isBatchingUpdates的值来决定的。
setState依赖于合成事件,合成事件指的是React并不是将click等事件直接绑定在DOM上面,而是采用事件冒泡的形式冒泡到顶层DOM上,类似于事件委托,然后React将事件封装给正式的函数处理运行和处理。说完了合成事件再回到setStatesetState的批量更新优化也是建立在合成事件上的,其会将所有的setState进行批处理,如果对同一个值进行多次 setStatesetState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时setState多个不同的值,在更新时也会对其进行合并批量更新,而在原生事件中,值会立即进行更新。
采用批量更新,简单来说就是为了提升性能,因为不采用批量更新,在每次更新数据都会对组件进行重新渲染,举个例子,让我们在一个方法内重复更新一个值。

this.setState({ msg: 1 });
this.setState({ msg: 2 });
this.setState({ msg: 3 });

事实上,我们真正想要的其实只是最后一次更新而已,也就是说前三次更新都是可以省略的,我们只需要等所有状态都修改好了之后再进行渲染就可以减少一些性能损耗。还有一个例子,如果更改了N个状态,其实只需要一次setState就可以将DOM更新到最新,如果我们更新多个值。

this.setState({ msg: 1 });
this.setState({ age: 2 });
this.setState({ name: 3 });

此处我们分三次修改了三种状态,但其实React只需要渲染一次,在setState批处理之后会将其合并,并进行一次re-render就可以将整个组件的DOM更新到最新,根本不需要关心这个setState到底是从哪个具体的状态发出来的。

那么还有一个问题,首先我们可以认同进行批处理更新对我们的性能是有益的,例如ChildParent都调用setState,我们不需要重新渲染Child两次。但是此时我们可能会想到一个问题,为什么不能如同Vue一样,Vue是在值更新之后触发setter然后进行更新,更新的过程同样也是采用异步渲染,也会将所有触发Watcherupdate进行去重合并再去更新视图,也就是说Vue是立即修改了值,而后再去更新视图的。也就是说,相比较于React,为什么不能在同样做批处理的情况下,立即将setState更新写入this.state而不等待协调结束。

任何一种解决方案都有权衡,对于Vue来说因为其是通过劫持了数据的setter过程,在使用的也是直接使用=直接赋值的,而在赋值之后进行视图的更新也是一个自然的过程,如果类似于React一样在=之后这个值依然没有变化,在直觉上是非常不符合常理的,虽然Vue是通过劫持setter实现的视图更新,但是做到如同React一样并不是不可能的,这是Vue采用的解决方案上的权衡,当然这只是可能的一个理由,说是问题的权衡,实际上还是需要对比React来看,对于React中要处理的问题,Vue自然会有自己解决方案的权衡,归根到底还是框架的设计哲学的问题。对于上面提出的在同样做批处理的情况下,立即将setState更新写入this.state而不等待协调结束的这个问题,dan给予了两个理由,在此简作总结,完整的英文版本还请看参考中的GitHub issue

保证内部数据统一

即使state是同步更新的,但props是不会的,在重新渲染父组件之前,无法知道props,如果同步执行此操作,批处理就会消失。现在React提供的对象statepropsrefs在内部是一致的。这意味着如果只使用这些对象,则可以保证它们引用完全协调的树,即使它是该树的旧版本。当仅使用state时,同步刷新的模式将起作用。

console.log(this.state.value); // 0
this.setState({ value: this.state.value + 1 });
console.log(this.state.value); // 1
this.setState({ value: this.state.value + 1 });
console.log(this.state.value); // 2

但是,假设需要提升此状态以在几个组件之间共享,因此将其移动到父级,也就是说有props参与到了传值,那么同步setState模式就会有问题,此时将state提升到了父组件,利用props将值传导子组件。

console.log(this.props.value); // 0
this.props.onIncrement();
console.log(this.props.value); // 0
this.props.onIncrement();
console.log(this.props.value); // 0

这是因为在这个解决方案中,this.state会立即刷新,而this.props不会,而且我们不能在不重新渲染父对象的情况下立即刷新this.props,这意味着我们将不得不放弃批处理的策略。还有更微妙的情况说明这如何破坏一致性的,例如这种方案正在混合来自props尚未刷新和state建议立即刷新的数据以创建新状态。在React中,this.statethis.props都只在协调和刷新之后更新,所以你会在refactoring之前和之后看到0被打印出来。这使得提升状态安全。在某些情况下这可能会带来不便,特别是对于来自更多OO背景的人来说,他们只想多次改变状态,而不是考虑如何在一个地方表示完整的状态更新,我可以理解这一点,尽管我确实认为从调试的角度来看,保持状态更新的集中更加清晰。总而言之,React模型并不总是会产生最简洁的代码,但它在内部是一致的,并确保提升状态是安全的。

启用并发更新

从概念上讲React的行为就好像每个组件都有一个更新队列,我们在这里讨论是否同步刷新state有一个前提那就是我们默认更新节点是遵循特定的顺序的,但是按默认顺序更新组件在以后的react中可能就变了。对于现在我们一直在谈论的异步渲染,我承认我们在传达这意味着什么方面做得不是很好,但这就是研发的本质:你追求一个在概念上看起来很有前途的想法,但只有在花了足够的时间之后才能真正理解它的含义。
对于这个理由,是React发展的一个方向。我们一直在解释异步渲染的一种方式是React可以根据setState()调用的来源分配不同的优先级:事件处理程序、网络响应、动画等。例如你现在正在打字,那么TextBox组件需要实时的刷新,但是当你在输入的时候,来了一个信息,这个时候可能让信息的渲染延迟到某个阈值,而不是因为阻塞线程而让输入卡顿。如果我们让某些更新具有较低优先级,我们可以将它们的渲染分成几毫秒的小块,这样用户就不会注意到它们。异步rendering不仅仅是性能上的优化,我们认为这是React组件模型可以做什么的根本性转变。例如,考虑从一个屏幕导航到另一个屏幕的情况,通常会在渲染新屏幕时显示一个导航器,但是如果导航速度足够快,闪烁并立即隐藏导航器会导致用户体验下降,更糟糕的是如果有多个级别的组件具有不同的异步依赖项例如数据、代码、图像等,您最终会得到一连串短暂闪烁的导航器。由于所有的DOM重排,这既在视觉上令人不快,又使您的应用程序在实践中变慢。如果当您执行一个简单的setState()来呈现不同的视图时,我们可以开始在后台呈现更新后的视图。如果您自己不编写任何协调代码,您可以选择在更新时间超过某个阈值时显示导航器,否则当整个新子树的异步依赖项是时让React执行无缝转换使满意。请注意,这只是可能的,因为this.state不会立即刷新,如果它被立即刷新,我们将无法开始在后台渲染视图的新版本,而旧版本仍然可见且可交互,他们独立的状态更新会发生冲突。

参考

https://www.jianshu.com/p/cc12e9a8052c
Https://juejin.cn/post/6844903636749778958
https://zh-hans.reactjs.org/docs/faq-state.html
https://blog.csdn.net/zz_jesse/article/details/118282921
https://blog.csdn.net/weixin_44874595/article/details/104270057

到此这篇关于React中setState同步异步场景的使用的文章就介绍到这了,更多相关React setState同步异步内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: React中setState同步异步场景的使用

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

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

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

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

下载Word文档
猜你喜欢
  • React中setState同步异步场景的使用
    目录setState同步异步场景描述原理保证内部数据统一启用并发更新参考setState同步异步场景 React通过this.state来访问state,通过this.setStat...
    99+
    2022-11-13
  • React中setState的使用与同步异步的使用
    在react中,修改状态如果直接使用this.state,不会引起组件的重新渲染,需要通过 this.setState来对组件的属性进行修改。 1、this.setState的两种...
    99+
    2022-11-11
  • React 中的 setState 是同步还是异步
    setState 是同步还是异步?肯定是异步的呀。 确定么?那看一下这段代码会打印什么: import { Component } from 'react'; class Dong ...
    99+
    2022-11-13
  • React中setState如何使用与如何同步异步
    这篇文章主要介绍“React中setState如何使用与如何同步异步”,在日常操作中,相信很多人在React中setState如何使用与如何同步异步问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”React中s...
    99+
    2023-06-14
  • React中setState同步和异步的示例分析
    这篇文章主要介绍React中setState同步和异步的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完! React起源于Facebook的内部项目。React的出现是革命性的创新,React的是一个...
    99+
    2023-06-15
  • 代码解析React中setState同步和异步问题
     React起源于Facebook的内部项目。React的出现是革命性的创新,React的是一个颠覆式的前端框架。在React官方这样介绍的它:一个声明式、高效、灵活的、创...
    99+
    2022-11-12
  • React中setState同步或异步问题的示例分析
    这篇文章主要为大家展示了“React中setState同步或异步问题的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“React中setState同步或异步问题的示例分析”这篇文章吧。1....
    99+
    2023-06-25
  • 关于React中setState同步或异步问题的理解
    目录1. setState同步?异步? 2. 表现为异步 1. React 合成事件 2. 生命周期函数 3. 表现为同步 1. 原生事件 2. setTimeout 4. setS...
    99+
    2022-11-12
  • React 中 setState 的异步操作案例详解
    目录前言React 中的 setState 为什么需要异步操作?什么时候setState会进行同步操作?前言 在使用state的时候, 如果我们企图直接...
    99+
    2022-11-13
  • ajax同步和异步的应用场景有哪些
    Ajax异步请求的主要应用场景包括:1. 实时更新数据:可以通过Ajax异步请求来更新页面上的数据,如即时聊天、动态通知等。2. 提...
    99+
    2023-09-13
    ajax
  • 详解Spring DeferredResult异步操作使用场景
    目录为什么使用DeferredResult?场景Demo代码测试1. 超时2. 进行setResult总结:为什么使用DeferredResult? API接口需要在指定时间内将异步...
    99+
    2022-11-12
  • Apache 服务器中 Python 同步对象的使用场景分析
    随着互联网的发展,越来越多的应用程序需要同时处理大量的并发请求。而在这种情况下,多线程编程和多进程编程是常用的解决方案。而 Python 作为一种高级编程语言,提供了许多同步对象来支持多线程编程和多进程编程。本文将介绍 。 线程锁 在...
    99+
    2023-10-29
    同步 对象 apache
  • Redux中异步action与同步action的使用
    异步action action:Object{} => 同步actionaction:function() => 异步action  同步action异步act...
    99+
    2022-11-13
  • Go并发同步Mutex典型易错使用场景
    目录Mutex的4种易错使用场景1.Lock/Unlock 不成对出现2.Copy 已使用的 Mutex3.重入4.死锁解决策略Mutex的4种易错使用场景 1.Lock/Unloc...
    99+
    2022-11-11
  • async/await异步应用的常用场景有哪些
    小编给大家分享一下async/await异步应用的常用场景有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前言async/a...
    99+
    2022-10-19
  • Python API 数组同步的应用场景有哪些?
    Python 是一种高级编程语言,它已成为数据科学和机器学习的主要语言之一。Python 的一个优点是它提供了许多强大的 API,用于处理数据和执行各种操作。在本文中,我们将讨论 Python API 数组同步的应用场景。 在 Pytho...
    99+
    2023-08-17
    api 数组 同步
  • Java中异步处理机制的应用场景有哪些
    Java中异步处理机制的应用场景有哪些?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。       通...
    99+
    2023-05-31
    java ava
  • 同步算法在ASP编程中的应用场景有哪些?
    ASP是一种广泛使用的Web开发技术,它允许开发人员通过使用服务器端脚本语言来创建动态网站。为了确保ASP应用程序的高效性和可靠性,同步算法是必不可少的一部分。本文将介绍同步算法在ASP编程中的应用场景,并提供一些演示代码。 数据库操作...
    99+
    2023-07-31
    编程算法 关键字 同步
  • PHP 同步 Laravel 数组的用途和场景是什么?
    在 Laravel 开发中,数组是一个非常常见的数据结构,而同步数组则是一种在多个任务间共享数据的一种方式。本文将探讨同步数组的用途和场景,并演示如何使用 PHP 同步 Laravel 数组。 一、同步数组的用途 同步数组主要用于多个任务...
    99+
    2023-09-18
    同步 laravel 数组
  • 使用Golang的同步机制优化高并发场景下的性能
    在高并发场景下,使用Golang的同步机制可以优化性能。以下是一些优化建议:1. 使用互斥锁(Mutex):在访问共享资源时使用互斥...
    99+
    2023-10-08
    Golang
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作