• 作者:老汪软件技巧
  • 发表时间:2024-10-05 15:01
  • 浏览量:

0.背景

什么是闭包?

为什么要存在闭包?

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

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

例1:

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}>changebutton>
    
  );
}
export default App;

经典例2:

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}>Incrementbutton>
      <button onClick={handleReset}>Resetbutton>
    div>
  );
}

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

(1) 对应原因(2) 解决方案

参考:

1.2 useEffect中的陷阱表现

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}>Incrementbutton>
    div>
  );
}

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

(1) 对应原因(2) 解决方案

useEffect中闭包表现对应参考文献:

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