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

相关推荐
mldong17 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排18 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby606119 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角20 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!20 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰20 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi1 天前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen1 天前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马1 天前
AI源码分析:拆解模型底层实现逻辑
前端
IT_陈寒1 天前
React子组件莫名其妙重渲染?你可能漏了这个Hook
前端·人工智能·后端