涉及先前状态的计算何时需要功能更新?

时间:2019-03-01 00:56:25

标签: javascript reactjs react-hooks

根据useState React Hook的documentation

  

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

给定

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

你可以写

setCount(prevCount => prevCount + 1);

我理解在setState中使用updater函数形式的原因,因为可能会批量处理多个调用。但是

  

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

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

setCount(count + 1);

Using the State Hook中的显示方式)。

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

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

1 个答案:

答案 0 :(得分:3)

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

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

您可以在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);

Edit Async state updates

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