【问题标题】:When are functional updates required for computations involving previous state?涉及先前状态的计算何时需要功能更新?
【发布时间】:2019-03-01 00:56:24
【问题描述】:

根据documentationuseState React Hook:

如果新状态是使用前一个状态计算的,你可以传递一个 函数到setState。该函数将接收先前的值, 并返回一个更新的值。

给定

const [count, setCount] = useState(initialCount);

你可以写

setCount(prevCount => prevCount + 1);

我理解使用 setState 的更新函数表单的原因,因为可能会批处理多个调用。不过

在后续重新渲染期间,useState 返回的第一个值 将始终是应用更新后的最新状态。

所以我不清楚为什么上面的例子不能写成

setCount(count + 1);

(这是它在Using the State Hook 中的呈现方式)。

是否存在必须使用 useState 挂钩的功能更新才能获得正确结果的情况?

(编辑:可能与https://github.com/facebook/react/issues/14259有关)

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    仍然需要功能更新语法的主要场景是在异步代码中。想象一下,在useEffect 中,您进行了某种 API 调用,当它完成时,您更新了一些也可以通过其他方式更改的状态。 useEffect 将在效果开始时关闭状态值,这意味着在 API 调用完成时,状态可能已过期。

    下面的示例通过单击按钮触发两个在不同时间完成的不同异步进程来模拟这种情况。一键立即更新计数;一个按钮在不同时间触发两个异步增量,而不使用功能更新语法(Naive 按钮);最后一个按钮使用功能更新语法(Robust button)在不同时间触发两个异步增量。

    您可以在 CodeSandbox 中使用它来查看效果。

    import React, { useState, useEffect } from "react";
    import ReactDOM from "react-dom";
    
    function App() {
      const [count, setCount] = useState(1);
      const [triggerAsyncIndex, setTriggerAsyncIndex] = useState(1);
      const [triggerRobustAsyncIndex, setTriggerRobustAsyncIndex] = useState(1);
      useEffect(
        () => {
          if (triggerAsyncIndex > 1) {
            setTimeout(() => setCount(count + 1), 500);
          }
        },
        [triggerAsyncIndex]
      );
      useEffect(
        () => {
          if (triggerAsyncIndex > 1) {
            setTimeout(() => setCount(count + 1), 1000);
          }
        },
        [triggerAsyncIndex]
      );
      useEffect(
        () => {
          if (triggerRobustAsyncIndex > 1) {
            setTimeout(() => setCount(prev => prev + 1), 500);
          }
        },
        [triggerRobustAsyncIndex]
      );
      useEffect(
        () => {
          if (triggerRobustAsyncIndex > 1) {
            setTimeout(() => setCount(prev => prev + 1), 1000);
          }
        },
        [triggerRobustAsyncIndex]
      );
      return (
        <div className="App">
          <h1>Count: {count}</h1>
          <button onClick={() => setCount(count + 1)}>Increment Count</button>
          <br />
          <button onClick={() => setTriggerAsyncIndex(triggerAsyncIndex + 1)}>
            Increment Count Twice Async Naive
          </button>
          <br />
          <button
            onClick={() => setTriggerRobustAsyncIndex(triggerRobustAsyncIndex + 1)}
          >
            Increment Count Twice Async Robust
          </button>
        </div>
      );
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    
    

    另一个可能需要功能更新的情况是,如果多个效果正在更新状态(即使是同步的)。一旦一个效果更新状态,另一个效果将查看过期状态。与异步场景相比,这种场景在我看来不太可能(并且在大多数情况下似乎是一个糟糕的设计选择)。

    【讨论】:

    • 非常感谢!旁注-在您的示例中,“Increment Count Twice Async Robust”按钮仅在第一次单击时才起作用(除非您随后单击“Increment Count Twice Async Naive”)。我认为第 49 行应该是setTriggerRobustAsyncIndex(triggerRobustAsyncIndex + 1)
    猜你喜欢
    • 2021-02-26
    • 2022-10-06
    • 2022-01-20
    • 2020-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-27
    • 2021-01-14
    相关资源
    最近更新 更多