Согласно документация для useState React Hook:
If the new state is computed using the previous state, you can pass a function to
setState. The function will receive the previous value, and return an updated value.
Так дано
const [count, setCount] = useState(initialCount);
ты можешь написать
setCount(prevCount => prevCount + 1);
Я понимаю причину использования формы функции обновления с setState, так как несколько вызовов могут быть объединены в пакеты. тем не мение
During subsequent re-renders, the first value returned by
useStatewill always be the most recent state after applying updates.
Поэтому мне не ясно, почему приведенный выше пример нельзя было записать как
setCount(count + 1);
(именно так это представлено в Использование хука состояния).
Есть ли случай, когда вы должны использовать функциональные обновления с хуком useState, чтобы получить правильный результат?
(Обновлено: возможно, связано с https://github.com/facebook/реагировать/issues/14259)



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Основные сценарии, когда синтаксис функционального обновления по-прежнему необходим, — это когда вы работаете в асинхронном коде. Представьте, что в useEffect вы выполняете какой-то вызов API, и когда он завершается, вы обновляете некоторое состояние, которое также можно изменить каким-либо другим способом. useEffect будет закрыто значение состояния на момент начала эффекта, что означает, что к моменту завершения вызова API состояние может быть устаревшим.
В приведенном ниже примере имитируется этот сценарий: нажатие кнопки запускает два разных асинхронных процесса, которые завершаются в разное время. Одна кнопка выполняет немедленное обновление счетчика; одна кнопка запускает два асинхронных приращения в разное время без использования функционального синтаксиса обновления (кнопка Naive); последняя кнопка запускает два асинхронных приращения в разное время, используя синтаксис функционального обновления (кнопка «Надежная»).
Вы можете поиграть с этим в 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);
Другой возможный сценарий, в котором могут потребоваться функциональные обновления, — это если несколько эффектов обновляют состояние (даже синхронно). Как только один эффект обновит состояние, другой эффект будет рассматривать устаревшее состояние. Этот сценарий кажется мне менее вероятным (и в большинстве случаев кажется плохим выбором дизайна), чем асинхронные сценарии.
Большое тебе спасибо! Примечание: в вашем примере кнопка «Увеличить счетчик дважды асинхронно, надежная» работает только при первом нажатии (если только вы не нажмете «Увеличить счетчик дважды, асинхронно, наивно»). Я думаю, что строка 49 должна быть
setTriggerRobustAsyncIndex(triggerRobustAsyncIndex + 1).