彻底搞懂setState到底是同步还是异步(三)

前言

书接上回,React 17 和 React 18 中的setState 都已摸清到底是怎么回事,但是里面的代码全部是基于class 组件的形式

但是现在 React 推荐的都是 hooks 写法,那 hooks 和 class 组件在处理 setState 上有什么区别吗?

话不多说,咱们直接上代码看下结果:

大家好,我是【前端探险家克鲁】。微信公众号、知乎、掘金、CSDN同名,欢迎查看我的个人简介,一起学习提升。

将面试题改造成hooks形式

jsx 复制代码
import { useState } from 'react';
import './App.css';

function App() {
  const [count, setCount] = useState(0);

  const handle = () => {
    setCount(1);
    console.log('count: ', count);

    setCount(2);
    console.log('count: ', count);

    setTimeout(() => {
      setCount(3);
      console.log('count: ', count);

      setCount(4);
      console.log('count: ', count);
    }, 0);

  };

  return (
    <>
      <div className='App'>
        <button onClick={handle}>count is {count}</button>
      </div>
    </>
  );
}

export default App;

看下在 react 17和 React 18下的运行结果:

怎么回事,都是 0,而且两个版本下表现一致,看起来像是都在批处理中

真的是这么回事吗?为了能更清楚的看到 React 的渲染行为,修改下上边的代码,在每次渲染都都打印下当前的 count值,添加如下代码:

jsx 复制代码
  useEffect(() => {
    console.log('render: 此时的count: ', count);
  });

再次查看输出结果:

分析下:

  • 在 React 17 下,render 打印了 3 次,说明组件重新渲染了 3 次,回顾下当时讲解 React 17 的 setState时,批处理重新渲染一次,setTimeout中是同步渲染,重新渲染两次,能对上
  • 在来看下 React 18 下,render 打印了 2 次,说明组件重新渲染了 2 次,在对比下前面讲解的 React18 的自动批处理功能,setTimeput外部批处理一次,内部批处理一次,重新渲染两次,也能对上
  • 那为什么这期间打印的 count 都是 0 呢?

解惑

闭包

首先需要先知道一个 js 的特性:闭包,下面是 mdn 中关于闭包的解释

闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。

简单说就是:内部函数可以访问外部函数的变量

组件 App是一个函数,handle 是 App内的函数,这也就形成了一个闭包,所以 handle 函数才能访问到 count 变量的值。

结论

因为在react中state的更新都遵从一个原则:**state**指向的内容是不可变的。
state 改变时不是改变当前内容而是指向了新的内容对象

所以在上面setCount 改变count 时,count 指向了新的内容对象

又因为在函数组件中,handle 函数与组件App 形成了闭包关系

这就导致handle 函数一直持有count 的引用而无法发生变化,所以无论延时多长时间,最终打印的count值一直是旧值,也就是 0

相关推荐
风骏时光牛马1 小时前
生成一个AI多模态标题,仅输出一条标题,无额外文字、问句、备选、配图、文案等任何附加内容,不需要其他的任务服务 只要标题。
前端
wangruofeng2 小时前
从 GIF 到 AVIF:网页图片格式演进与 2026 年选型指南
前端·架构
清晨Coding2 小时前
为什么用 React Developer Tools 查看组件树这么乱?
前端·react.js
johnjincaaaa3 小时前
猿人学第 18 题「jsvmp 洞察先机」逆向通关实录
javascript·后端
鶴哥只手遮天3 小时前
《Electron 45.2.3 内核解剖》第 4 章 补丁体系:380 个补丁改了什么
前端·electron
FungLeo5 小时前
成为全栈·Vue 管理后台篇·分类树与标签管理:层级数据如何落到管理界面
前端·javascript·vue.js·分类树·naive ui·标签管理·前端数据结构
总要学点什么6 小时前
第108篇:三维特效-图片轨迹线效果——把贴图滚成一串箭头,想用它的本色反而整条消失
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
Lank_M8 小时前
白底都占八成,PNG 为何只在图表页赢过 JPG
前端
张一凡938 小时前
给 Coding Agent 加一道"架构闸门":Jin 是如何做到"先审架构、再写代码、不漂移"的
前端·后端·agent
Linye8 小时前
AI 写的 HTML 原型全是空壳?聊聊真实 DOM 交互避坑
前端·人工智能