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是因为考虑在更新过程中,存在优先级临时插队的"中断"情况,因此需要中断的时候及时存储对应当前计算出来的"临时值"。

相关推荐
GIS程序媛—椰子8 分钟前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_00114 分钟前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端17 分钟前
Content Security Policy (CSP)
前端·javascript·面试
木舟100921 分钟前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤439131 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢1 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安1 小时前
前端第二次作业
前端·css·css3
啦啦右一1 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习
半开半落1 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt