Я працюю над React.
TLDR:
Але чи можете ви довіряти React для оновлення стану в тому ж порядку, в якому вимагається setState
Так.
Так.
Порядок оновлень завжди дотримується. Чи ви бачите проміжний стан "між" ними чи ні, залежить від того, ви знаходитесь всередині партії чи ні.
Наразі (React 16 і новіші версії) лише оновлення всередині обробників подій React за замовчуванням пакетні . Існує нестабільний API для примусового використання пакетів поза обробниками подій у рідкісних випадках, коли це потрібно.
У майбутніх версіях (ймовірно, React 17 і пізніших версіях) React створить пакетні оновлення за замовчуванням, тому вам не доведеться думати про це. Як завжди, ми будемо повідомляти про будь-які зміни щодо цього в блозі React та у примітках до випуску.
Ключовим моментом для розуміння цього є те, що незалежно від того, скільки setState()
викликів у кількох компонентах ви виконуєте всередині обробника подій React , вони завершать лише одне повторне відображення в кінці події . Це має вирішальне значення для хорошої роботи у великих програмах, оскільки якщо Child
і Parent
кожен виклик setState()
під час обробки події клацання, ви не хочете повторно рендерізувати Child
двічі.
В обох ваших прикладах setState()
дзвінки відбуваються всередині обробника подій React. Тому вони завжди зливаються разом в кінці події (і проміжного стану ви не бачите).
Оновлення завжди неглибоко об'єднуються в порядку їх виникнення . Отже, якщо перше оновлення є {a: 10}
, друге є {b: 20}
, а третє - {a: 30}
буде наданим станом {a: 30, b: 20}
. Останнє оновлення до того ж ключа стану (наприклад, як a
у моєму прикладі) завжди "виграє".
this.state
Об'єкт оновлюється , коли ми знову зробити користувальницький інтерфейс в кінці партії. Отже, якщо вам потрібно оновити стан на основі попереднього стану (наприклад, збільшення числа лічильника), вам слід використовувати функціональну setState(fn)
версію, яка дає вам попередній стан, а не читання з this.state
. Якщо вам цікаво міркування для цього, я це глибоко пояснив у цьому коментарі .
У вашому прикладі ми б не побачили "проміжний стан", оскільки ми знаходимося в обробці подій React, де включено пакетне завантаження (адже React "знає", коли ми закінчуємо цю подію).
Однак, як у React 16, так і в більш ранніх версіях, поки що немає пакетної обробки за межами обробників подій React . Тож якби у вашому прикладі замість цього був обробник відповідей AJAX handleClick
, кожен setState()
би оброблявся негайно, як це відбувається. В цьому випадку, так, ви б бачити проміжний стан:
promise.then(() => {
// We're not in an event handler, so these are flushed separately.
this.setState({a: true}); // Re-renders with {a: true, b: false }
this.setState({b: true}); // Re-renders with {a: true, b: true }
this.props.setParentState(); // Re-renders the parent
});
Ми розуміємо, що незручно, що поведінка відрізняється залежно від того, перебуваєте ви в обробці подій чи ні . Це зміниться у майбутній версії React, яка буде пакетно застосовувати всі оновлення за замовчуванням (та надавати API для входу для синхронного очищення змін). Поки ми не переключимо поведінку за замовчуванням (можливо, в React 17), існує API, який можна використовувати для примусового створення пакетів :
promise.then(() => {
// Forces batching
ReactDOM.unstable_batchedUpdates(() => {
this.setState({a: true}); // Doesn't re-render yet
this.setState({b: true}); // Doesn't re-render yet
this.props.setParentState(); // Doesn't re-render yet
});
// When we exit unstable_batchedUpdates, re-renders once
});
Усі обробники подій React запускаються в усі, unstable_batchedUpdates
і саме тому вони замовчуються. Зауважте, що загортання оновлення у unstable_batchedUpdates
два рази не впливає. Оновлення видаються, коли ми закінчуємо зовнішній unstable_batchedUpdates
виклик.
Цей API є "нестабільним" в тому сенсі, що ми його видалимо, коли пакетне ввімкнення вже включено за замовчуванням. Однак ми не будемо видаляти його в незначній версії, тому ви можете спокійно розраховувати на нього до React 17, якщо вам потрібно буде примусово виконувати пакетну групу в деяких випадках поза обробниками подій React.
Підводячи підсумок, це заплутана тема, оскільки React за замовчуванням здійснює партії лише в обробниках подій. Це зміниться в майбутніх версіях, і поведінка тоді буде більш відвертою. Але рішення полягає в тому, щоб не купувати менше , це робити більше за замовчуванням. Ось що ми будемо робити.