【问题标题】:React useState hook does not seem to update the state on explicitly calling setter functionReact useState 钩子似乎没有在显式调用 setter 函数时更新状态
【发布时间】:2020-03-24 19:23:17
【问题描述】:

这是写的代码

function App() {

  const [number, setNumber] = useState(1);
  const mapper = {
    1: 'One',
    2: 'two',
    3: 'three'
  }
  const setFirst = () => {
    setNumber(1);
    console.log(number);
    console.log(mapper[number]);
  }
  const setSecond = () => {
    setNumber(2);
    console.log(number);
    console.log(mapper[number]);
  }
  const setThird = () => {
    setNumber(3);
    console.log(number);
    console.log(mapper[number]);
  }
  return (
    <div>
      <button onClick={() => { setFirst(); }}>One</button>

      <button onClick={() => { setSecond() }} >Two</button>

      <button onClick={() => { setThird(); }} >Three</button>
    </div>
  );
}

预期: 点击setFirst(),数字应设置为1。 点击setSecond(),数字应设置为2。 点击setThird(),数字应设置为3。

发生了什么

按顺序点击 setFirst() -&gt; setSecond() -&gt; setThird() 以重复的方式

输出:

1
One
1
One
2
Two
3
Three
1
One

预期输出:

1
One
2
Two
3
Three
1
One
2
Two

有人可以帮我解决这个问题吗?我需要帮助找出错误所在。

【问题讨论】:

  • 状态更新是异步的。在console.log之前状态还没来得及更新
  • @Chris console.log() 只是一个例子。如果我想使用该值做某事,我应该在 then() 块内还是在 useEffect() 中做
  • 使用useEffect

标签: javascript reactjs react-hooks


【解决方案1】:

正如 Chris 在评论中所说,setNumber 是一个异步函数,所以它的更新在执行后是不可见的。

此外,您应该知道,在每次渲染时,组件内的每个方法都“堆叠”在其当前闭包内。让我详细说明:

  1. 您渲染组件,它返回button(假设只是第一个)。当button 被渲染时,由于setFirst 链接到它,你可以想象一个房间正在被创建,其中setFirst 中使用的所有外部变量都被复制。因此,由于setFirst 使用变量numbermapper,因此在这个“房间”内创建了它们的副本;
  2. 当您最终单击setFirst 时,您运行setNumber。这个setNumber确实不会更新setFirst房间内的number,但它会更新将在下一个渲染阶段使用的number

这个例子是为了让你明白为什么当你点击setSecond时,你会被记录1个:当setSecond方法被初始化为那个渲染时,number仍然是1

【讨论】:

  • 感谢您的详细解释。我得到了它。由于setNumber() 是异步的,这不会立即反映在console.log() 中,即使它在下一行。如果我在一段时间后再次调用setFirst(),它会显示预期值。
  • 是的。不过,我确实想知道,一段时间后,你的意思是你使用setTimeout:如果是这样,你不应该,但你应该使用useEffect
  • 我只是想看看我的想法是否与setTimeout() 相符。有效。然后我做了这个。 useEffect(() =&gt; { console.log(currentPage) }, [currentPage]); 它反映了正确的值
  • 假设 currentPagenumber,这是使用 React Hooks 的正确方法。您应该始终认为“cause-effect”:cause 位于依赖项数组中,而 effect 是函数:您想要 当currentPage 发生变化时,我会那样做。因此,您使用useEffect,将原因效果分开。
【解决方案2】:

当事件循环中的 setter 函数时。有一个异步函数。您可以让成为sync 函数。但大多数情况下我们不建议这样做,所以我更喜欢使用回调来做到这一点。

setNumber(2, () => {
  console.log('');
})

Anti-Pattern 同步方式

Promise.resolve().then(() => {
  setNumber(2);
  console.log(2);
})

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-24
    • 1970-01-01
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 2023-01-18
    • 1970-01-01
    • 2020-08-20
    相关资源
    最近更新 更多