Зачем нужен React useState с функциональной формой обновления?

Я читаю React Hook documentation о функциональных обновлениях и 9X_useimperativeref вижу эту цитату:

Кнопки «+» и «-» используют 9X_use-ref функциональную форму, поскольку обновленные значение 9X_usecallback основано на предыдущем значении

Но я не могу 9X_react.js понять, для каких целей требуются функциональные 9X_use-state обновления и в чем разница между ними и 9X_use-ref прямым использованием старого состояния 9X_react-component при вычислении нового состояния.

Зачем вообще нужна функциональная форма обновления для функций обновления React useState Hook? В каких примерах мы можем четко увидеть разницу (поэтому использование прямого обновления приведет к ошибкам)?

Например, если 9X_react-component я изменю этот пример из документации

function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count}    </> ); } 

для 9X_reactjs прямого обновления count:

function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count}    </> ); } 

Я не вижу разницы в 9X_use-ref поведении и не могу представить себе случай, когда 9X_reactjs счетчик не будет обновлен (или не будет 9X_usecallback самым последним). Поскольку всякий раз, когда 9X_use-state счетчик изменяется, будет вызываться новое 9X_usecallback закрытие для onClick, захватывающее самый последний 9X_useimperativeref count.

48
0
5
Общее количество ответов: 5

Ответ #1

Ответ на вопрос: Зачем нужен React useState с функциональной формой обновления?

Обновление состояния в React выполняется 9X_use-effect асинхронно. Так что возможно, что в count будет 9X_usecallback старое значение, когда вы обновите его в 9X_react-jsx следующий раз. Сравните, например, результат 9X_react.js этих двух примеров кода:

function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count}   </> ); } 

и

function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count}   </> ); } 

35
2

  • Именно по этой причине - пользователь может щелкнуть + и - пер ...

Ответ #2

Ответ на вопрос: Зачем нужен React useState с функциональной формой обновления?

Я недавно столкнулся с необходимостью в 9X_react-usememo этом. Например, предположим, что у вас есть 9X_usecallback компонент, который заполняет массив некоторым 9X_react-hooks количеством элементов и может добавлять 9X_use-ref в этот массив в зависимости от какого-либо 9X_use-ref действия пользователя (например, в моем 9X_useimperativeref случае, я загружал фид по 10 элементов за 9X_react-usememo раз, поскольку пользователь продолжал прокручивать 9X_useimperativeref экран вниз. код выглядел примерно так:

function Stream() { const [feedItems, setFeedItems] = useState([]); const { fetching, error, data, run } = useQuery(SOME_QUERY, vars); useEffect(() => { if (data) { setFeedItems([...feedItems, ...data.items]); } }, [data]); // <---- this breaks the rules of hooks, missing feedItems ...  ... 

Очевидно, вы 9X_react-jsx не можете просто добавить feedItems в список 9X_react-component зависимостей в хуке useEffect, потому что 9X_react-component вы вызываете в нем setFeedItems, так что 9X_react вы получите цикл.

функциональное обновление 9X_react-jsx спешит на помощь:

useEffect(() => { if (data) { setFeedItems(prevItems => [...prevItems, ...data.items]); } }, [data]); // <--- all good now 

23
0

Ответ #3

Ответ на вопрос: Зачем нужен React useState с функциональной формой обновления?

“state update is asynchronous in React” answer вводит в заблуждение, как и некоторые комментарии 9X_useimperativeref под ним. Мое мышление также было неверным, пока 9X_use-state я не углубился в это. Вы правы, это редко 9X_react-usememo требуется.

Ключевая идея обновлений функционального 9X_reactjs состояния заключается в том, что состояние, от 9X_react которого зависит новое состояние, может 9X_react-dom быть устаревшим. Как состояние устаревает? Давайте 9X_reactjs развеем некоторые мифы о нем:

  • Миф: Состояние может быть изменено под вас во время обработки события.
    • Факт: Цикл обработки событий ECMAScript выполняет только одно действие за раз. Если вы запускаете обработчик, ничего не работает вместе с ним.
  • Миф: Двойной быстрый щелчок (или любое другое быстрое действие пользователя) может привести к пакетному обновлению состояния от обоих вызовов обработчика.
    • Факт: React гарантированно не выполняет пакетное обновление более чем одного события, инициированного пользователем. Это верно даже для React 18, который выполняет больше пакетной обработки, чем предыдущие версии. Вы можете положиться на рендеринг между обработчиками событий.

From the React Working Group:

Примечание. React 9X_react-usememo выполняет пакетное обновление только тогда, когда 9X_reactjs это безопасно. Например, React гарантирует, что 9X_react-usememo для каждого инициированного пользователем события, такого как щелчок или нажатие клавиши, DOM полностью обновляется перед следующим событием. Это гарантирует, например, что форма, которая 9X_use-ref отключается при отправке, не может быть 9X_use-effect отправлена ​​дважды.

Итак, когда вы получаете устаревшее состояние?

Вот основные 3 случая, которые 9X_use-reducer я могу придумать:

Несколько обновлений состояния в одном обработчике

Это уже упоминавшийся случай, когда 9X_react-component вы устанавливаете одно и то же состояние 9X_useimperativeref несколько раз в одном и том же обработчике 9X_react-component и зависите от предыдущего состояния. Как 9X_use-ref вы указали, этот случай довольно надуманный, потому 9X_use-effect что это явно выглядит неправильно:

  

Более 9X_react.js вероятным случаем является вызов нескольких 9X_react-jsx функций, каждая из которых обновляет одно 9X_react-dom и то же состояние и зависит от предыдущего 9X_react.js состояния. Но это все равно странно, было 9X_use-ref бы разумнее сделать все вычисления, а затем 9X_react-component установить состояние один раз.

Асинхронные обновления состояния в обработчике

Например:

  

Это 9X_useimperativeref не так очевидно неправильно. Состояние можно 9X_react-usememo изменить между вызовом doSomeApiCall и его разрешением. В 9X_react-component этом случае обновление состояния действительно 9X_react-dom асинхронно, но вы сделали его таким, а не 9X_react-hooks React!

Функциональная форма исправляет это:

  

Обновление состояния в useEffect

G Gallegos's answer указал 9X_useimperativeref на это для useEffect в целом, а letvar's answer указал на это для 9X_useimperativeref useEffect с requestAnimationFrame. Если вы обновляете состояние на основе 9X_use-effect предыдущего состояния в useEffect, помещение этого 9X_usecallback состояния в массив зависимостей (или отказ 9X_react-component от использования массива зависимостей) — это 9X_use-reducer рецепт бесконечных циклов. Вместо этого 9X_use-ref используйте функциональную форму.

Обзор

Вам не 9X_react-usememo нужна функциональная форма для обновления 9X_react состояния на основе предыдущего состояния, если 9X_react-hooks вы делаете это 1. в обработчике событий, инициируемых 9X_react-dom пользователем 2. один раз для каждого обработчика 9X_react-dom для каждого состояния и 3. синхронно. Если 9X_use-state вы нарушите какое-либо из этих условий, вам 9X_reactjs потребуются функциональные обновления.

Некоторые 9X_use-reducer люди могут предпочесть всегда использовать 9X_use-state функциональные обновления, поэтому вам не 9X_use-reducer нужно беспокоиться об этих условиях. Другие 9X_usecallback могут предпочесть более короткую форму для 9X_react ясности, когда это безопасно, что верно 9X_react-component для многих обработчиков. В этот момент это 9X_react.js личные предпочтения/стиль кода.

Историческая справка

Я изучил 9X_usecallback React до хуков, когда состояние было только 9X_use-reducer у компонентов класса. В компонентах класса 9X_use-ref «несколько обновлений состояния в одном 9X_react-dom и том же обработчике» не выглядит так явно 9X_react-usememo неправильно:

  

Поскольку состояние является 9X_usecallback переменной экземпляра, а не параметром функции, все 9X_react.js выглядит нормально, если только вы не знаете, что 9X_react-jsx setState группирует вызовы в одном и том же обработчике.

На 9X_react-jsx самом деле в React <= 17 это сработает 9X_use-ref нормально:

 setTimeout(() => { this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); }, 1000); 

Поскольку это не обработчик событий, React 9X_react-hooks выполняет повторный рендеринг после каждого 9X_use-ref вызова setState.

React 18 вводит пакетную обработку 9X_reactjs для этого и подобных случаев. Это полезное 9X_react-dom улучшение производительности. Недостатком 9X_reactjs является то, что он ломает компоненты класса, которые 9X_reactjs полагаются на описанное выше поведение.

Ссылки

  • React Working Group discussion
  • ehab’s answer, где также упоминаются два случая, когда необходимы функциональные обновления.

11
0

Ответ #4

Ответ на вопрос: Зачем нужен React useState с функциональной формой обновления?

Я ответил на similar question вот так, и он был закрыт, потому 9X_react.js что это был канонический вопрос, о котором 9X_useimperativeref я не знал. Посмотрев ответы, я решил повторно 9X_react-jsx опубликовать свой ответ здесь, так как я 9X_usecallback думаю, что он добавляет некоторую ценность.

Если 9X_use-effect ваше обновление зависит от предыдущего значения, найденного 9X_reactjs в состоянии, вам следует использовать функциональную 9X_use-reducer форму. Если в этом случае вы не используете 9X_use-reducer функциональную форму, ваш код когда-нибудь 9X_reactjs сломается.

Почему и когда ломается

Функциональные компоненты React 9X_use-state - это просто замыкания, значение состояния, которое 9X_use-reducer у вас есть в замыкании, может быть устаревшим 9X_react - это означает, что значение внутри замыкания 9X_use-state не соответствует значению, которое находится 9X_usecallback в состоянии React для этого компонента, это 9X_react может случаются в следующих случаях:

1 - асинхронные 9X_react-usememo операции (In this example нажмите медленное добавление, а 9X_react-usememo затем нажмите несколько раз кнопку добавления, позже 9X_react-jsx вы увидите, что состояние было сброшено 9X_react до того, что было внутри закрытия, когда 9X_usecallback была нажата кнопка медленного добавления)

const App = () => { const [counter, setCounter] = useState(0); return ( <> 
counter {counter}

</> ); };

2- Когда 9X_react-jsx вы вызываете функцию обновления несколько 9X_reactjs раз в одном и том же закрытии

const App = () => { const [counter, setCounter] = useState(0); return ( <> 
counter {counter}

</> ); }

9
0

Ответ #5

Ответ на вопрос: Зачем нужен React useState с функциональной формой обновления?

Другой вариант использования функциональных 9X_reactjs обновлений с setState - requestAnimationFrame с перехватчиками реакции. Подробная 9X_use-ref информация доступна здесь - https://css-tricks.com/using-requestanimationframe-with-react-hooks/

Таким образом, обработчик 9X_react-hooks для requestAnimationFrame часто вызывается, что приводит к неправильному 9X_react-dom значению count, когда вы выполняете setCount(count+delta). С другой 9X_reactjs стороны, использование setCount(prevCount => prevCount + delta) дает правильное 9X_use-ref значение.

1
0