React Hooks 闭包陷阱

0.背景

什么是闭包?

为什么要存在闭包?

1.不同React Hooks中的闭包陷阱的表现

1.1 useState中的陷阱表现

具体问题表现场景是:在异步回调中获取不到最新的state状态值

例1:

js 复制代码
import React, { useState, useEffect } from 'react';

const App = () => {
  const [arr, setArr] = useState([0]);

  useEffect(() => {
    console.log(arr);
  }, [arr]);

  const handleClick = () => {
    Promise.resolve().then(() => {
      setArr([...arr, 1]); // 此时赋值前 arr 为:[0],异步回调中更新了state状态
    })
      .then(() => {
        setArr([...arr, 2]); // 此时赋值前 arr 为旧状态仍然为:[0],在第二个异步回调中拿到的state值不是前面更新过的
      });
  }
  
  // 我们希望打印的是[0], [0,1], [0,1,2]
  // 实际上打印的而是[0], [0,1], [0,1]
  return (
    <>
      <button onClick={handleClick}>change</button>
    </>
  );
}

export default App;

经典例2:

js 复制代码
function Counter() {
  const [count, setCount] = useState(0);
  const handleClick = () => {
    setTimeout(() => {
      setCount(count + 1);
    }, 1000);
  };
  const handleReset = () => {
    setCount(0);
  };
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
      <button onClick={handleReset}>Reset</button>
    </div>
  );
}

我们每次点击之后,开启一个异步的定时器回调,然后在定时器中执行更新setState函数。假设定时器的执行间隔是1s。那么我在1s内连续点击5次,对应的函数回调会设置5个定时器回调函数,每隔1s就会执行setState操作来更新state状态。每次回调应该拿到最新的state状态值。

(1) 对应原因

(2) 解决方案

参考:www.cnblogs.com/hymenhan/p/...

1.2 useEffect中的陷阱表现

js 复制代码
function App() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    const timer = setInterval(() => {
      console.log(count);
    }, 1000);
    return () => clearInterval(timer);
  }, []);
  
  const handleClick = () => {
    setCount(count + 1);
  };
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
}

在这个例子中,我们在点击事件回调中使用setState来更新state状态,然后 我们使用了 useState 和 useEffect Hooks。在 useEffect 回调函数内部,我们使用了一个 setInterval 函数来输出 count 状态变量。然而,由于 useEffect 只会在组件首次渲染时执行一次,因此闭包中的 count 变量始终是首次渲染时的变量,而不是最新的值。

(1) 对应原因

(2) 解决方案

useEffect中闭包表现对应参考文献: segmentfault.com/a/119000004...

2.总结

对于这个问题能给我带来什么?我们需要注意在异步中更新state状态后,后续的是否可以拿到最新的state状态值。

问题1:组件每次更新UI的时候,拿到的state是从哪里获取的,是在hook-useState中的memoizedState中拿的吗?

问题2:hook如何获取数据

问题3:hook如何更新数据?

当我们使用useState的时候,返回的第二个setState函数可以认为是dispatch函数,我们使用这个函数来更新state状态,就可以实现视图UI的更新。 那么我们使用setState的时候发生了什么呢?首先,先考虑state的数据的更新,在考虑视图的更新。 数据方面,调用一次setState会对应创建一个update对象

hooks节点中的memoizedState和baseState,在存储的数据一定是相同的吗?如果不同,为什么要这么设置?

答:basestate是当前组件在更新过程中,基于已更新的state的临时存储值。而memoizedstate是当前组件所有的setState更新完毕之后最终存储的state最终值。UI视图上渲染的就是拿的这个memoizedState值。之所以要额外设置baseState是因为考虑在更新过程中,存在优先级临时插队的"中断"情况,因此需要中断的时候及时存储对应当前计算出来的"临时值"。

相关推荐
hunterandroid4 小时前
HarmonyOS WebSocket 实战:断线重连、心跳保活与连接状态机设计
前端
hunterandroid4 小时前
StateFlow 与 SharedFlow 的边界:状态与事件的正确建模
android·前端
心念科技4 小时前
1、搜索表单 xnSearch(基于:心念后台,后端 Java 21 + Spring Boot 4 + Spring Cloud,前端提供 ReactVue3+TS、Vue3+JS、Vue2+JS
前端
计算机魔术师5 小时前
Dwarkesh Patel 对 OpenAI/Hugging Face 事件的爆款解读被指危险误导
前端
自进化Agent智能体6 小时前
Hermes GitHub PR 审查 —— 自动化代码评审
前端
涛涛ing7 小时前
OpenAI Astra 泄露:零样本生成 3D 网页,前端开发者慌了吗?
前端
ssshooter8 小时前
现在网页都能提供 MCP 了?!
前端·人工智能·程序员
杉氧8 小时前
状态管理变迁史:为什么我们放弃了 Redux 选择 Zustand?
android·前端·react native
cidy_988 小时前
OpenCode 手动配置火山方舟 Agent Plan 教程
前端
鹏多多8 小时前
PC 网站接入微信登录,这 10 个坑我替你踩完了!
前端·javascript·vue.js