Когда функциональные обновления требуются для вычислений с предыдущим состоянием? - PullRequest
0 голосов
/ 01 марта 2019

Согласно документации для useState React Hook:

Если новое состояние вычисляется с использованием предыдущего состояния, вы можете передать функцию в setState,Функция получит предыдущее значение и вернет обновленное значение.

Таким образом, учитывая

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

, вы можете написать

setCount(prevCount => prevCount + 1);

Я понимаю причинудля использования формы функции обновления с setState, так как несколько вызовов могут быть пакетированы.Однако

Во время последующих повторных визуализаций первое значение, возвращаемое useState, всегда будет самым последним состоянием после применения обновлений.

Так что мне не яснопочему приведенный выше пример не может быть записан как

setCount(count + 1);

(как это представлено в Использование State Hook ).

Есть ли случай, когда выдолжны использовать функциональные обновления с крюком useState, чтобы получить правильный результат?

(Редактировать: Возможно, связано с https://github.com/facebook/react/issues/14259)

1 Ответ

0 голосов
/ 01 марта 2019

Основные сценарии, когда синтаксис функционального обновления все еще необходим, - это когда вы находитесь в асинхронном коде.Представьте себе, что в useEffect вы выполняете какой-то вызов API, а когда он заканчивается, вы обновляете некое состояние, которое также может быть изменено другим способом.useEffect будет закрываться по значению состояния во время начала эффекта, что означает, что к моменту завершения вызова API состояние может быть устаревшим.

В приведенном ниже примере имитируется этот сценарийнажатие кнопки вызывает два разных асинхронных процесса, которые завершаются в разное время.Одна кнопка выполняет немедленное обновление счета;одна кнопка запускает два асинхронных приращения в разное время без использования синтаксиса функционального обновления (кнопка «Наивный»);последняя кнопка запускает два асинхронных приращения в разное время с использованием синтаксиса функционального обновления (кнопка Robust).

Вы можете поиграть с этим в 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

Другой возможный сценарий, в котором могут потребоваться функциональные обновления, - это если несколько эффектов обновляют состояние (даже если синхронно).Как только один эффект обновляет состояние, другой эффект будет смотреть на устаревшее состояние.Этот сценарий кажется мне менее вероятным (и в большинстве случаев кажется плохим выбором дизайна), чем асинхронные сценарии.

...