Чи підтримує React замовлення на оновлення стану?


140

Я знаю, що React може виконувати оновлення стану асинхронно та пакетно для оптимізації продуктивності. Тому ви ніколи не можете довіряти державі, що оновлюється після дзвінка setState. Але ви можете довіряти Реагувати постійно оновлювався в тому ж порядку , як setStateназивається для

  1. той же компонент?
  2. різні компоненти?

Спробуйте натиснути кнопку в наступних прикладах:

1. Чи існує ймовірність, що а є хибним, а b - правдою для:

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = { a: false, b: false };
  }

  render() {
    return <Button onClick={this.handleClick}/>
  }

  handleClick = () => {
    this.setState({ a: true });
    this.setState({ b: true });
  }
}

2. Чи існує коли-небудь можливість, що а є хибним, а b - правдою для:

class SuperContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = { a: false };
  }

  render() {
    return <Container setParentState={this.setState.bind(this)}/>
  }
}

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = { b: false };
  }

  render() {
    return <Button onClick={this.handleClick}/>
  }

  handleClick = () => {
    this.props.setParentState({ a: true });
    this.setState({ b: true });
  }
}

Майте на увазі, що це надзвичайно спрощені випадки мого використання. Я усвідомлюю, що я можу це зробити по-різному, наприклад, оновлення обох параметрів стану одночасно в прикладі 1, а також виконання другого оновлення стану в зворотному режимі виклику до першого оновлення стану в прикладі 2. Однак це не моє питання, і мене цікавить лише те, чи існує чітко визначений спосіб виконання React цих оновлень стану, нічого іншого.

Будь-яка відповідь, підкріплена документацією, дуже вдячна.


1
Дивіться це: stackoverflow.com/a/36087156/3776299
— helb

3
це не здається безглуздим питанням, ви також можете задати це питання на github випуску сторінки реагування, і дан abramov зазвичай дуже корисний. Коли у мене виникли такі хитрі запитання, я б задавав, і він відповів би. Погано в тому, що такі питання не публікуються публічно, як в офіційних документах (так що інші також можуть легко отримати доступ до нього). Я також відчуваю, що в офіційних документах React бракує широкого висвітлення деяких тем, таких як тема вашого запитання тощо.
— giorgim

Для прикладу візьмемо це: github.com/facebook/react/isissue/11793 , я вважаю, що матеріали, обговорені в цьому випуску, були б корисними для багатьох розробників, але цей матеріал не є в офіційних документах, тому що люди з ФБ вважають це просунутим. Те саме стосується інших речей, можливо. Я думаю, що офіційна стаття під назвою "на кшталт" управління державою реагує в глибині "або" підводні камені управління державою ", які вивчають усі кутові випадки управління державою, як у вашому питанні, було б непогано. можливо, ми можемо підштовхнути розробників ФБ до розширення документації з такими матеріалами :)
— giorgim

Thre - це посилання на чудову статтю про медіа в моєму питанні. Він повинен охоплювати 95% випадків використання державою. :)
— Міхал

2
@Michal, але ця стаття все ще не відповідає на це запитання IMHO
— giorgim

Відповіді:


336

Я працюю над 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 за замовчуванням здійснює партії лише в обробниках подій. Це зміниться в майбутніх версіях, і поведінка тоді буде більш відвертою. Але рішення полягає в тому, щоб не купувати менше , це робити більше за замовчуванням. Ось що ми будемо робити.


1
Один із способів "завжди правильно виконати замовлення" - це створити тимчасовий об'єкт, призначити різні значення (наприклад obj.a = true; obj.b = true), а потім в кінці просто зробити this.setState(obj). Це безпечно незалежно від того, знаходитесь ви в обробці подій чи ні. Це може бути акуратним трюком, якщо ви часто виявляєте помилку встановлення стану кілька разів поза обробниками подій.
— Кріс

Таким чином, ми не можемо покластися на те, що партія обмежиться лише одним обробником подій - як ви зрозуміли, принаймні тому, що це скоро не відбудеться. Тоді ми повинні використовувати setState з функцією оновлення, щоб отримати доступ до останнього стану, правда? Але що робити, якщо мені потрібно використовувати якийсь state.filter, щоб зробити XHR для читання деяких даних, а потім перевести їх у стан? Схоже, мені доведеться помістити XHR з відкладеним зворотним зворотом (і, отже, побічним ефектом) в оновлення. Тоді це вважається найкращою практикою?
— Максим Гумеров

1
І, до речі, це також означає, що ми не повинні читати з цього сайту. єдиний розумний спосіб прочитати деякий стан.X - це прочитати його у функції оновлення з його аргументу. І писати в this.state також небезпечно. Тоді навіщо взагалі дозволяти доступ до this.state? Вони, можливо, можуть задавати окремі запитання, але в основному я просто намагаюся зрозуміти, чи отримав я пояснення правильно.
— Максим Гумеров

10
цю відповідь слід додати до документації на reactjs.org
— Дін Джон,

2
Чи можете ви, будь ласка, уточнити в цій публікації, якщо "Реактор обробника подій" включає componentDidUpdateта інші зворотні дзвінки з життєвого циклу станом на React 16? Спасибі заздалегідь!
— Іван

6

Це насправді досить цікаве питання, але відповідь не повинна бути надто складною. Є ця чудова стаття на носії, на яку є відповідь.

1) Якщо ви це зробите

this.setState({ a: true });
this.setState({ b: true });

Я не думаю , що там буде ситуація , коли aбуде trueі bбуде falseз - за дозуванням .

Однак якщо bце залежить відa того, то справді може виникнути ситуація, коли ви не отримаєте очікуваного стану.

// assuming this.state = { value: 0 };
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});

Після обробки всіх вищевказаних дзвінків this.state.valueбуде 1, а не 3, як ви очікували.

Про це йдеться у статті: setState accepts a function as its parameter

// assuming this.state = { value: 0 };
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));

Це нам дасть this.state.value === 3


Що робити , якщо this.state.valueоновлюється і обробники подій (де setStateдозується) і AJAX зворотних викликів (де setStateце НЕ дозується). У обробниках подій я б використовував updater functionзавжди, щоб бути впевненим, що я оновлюю стан, використовуючи поточно оновлений стан, наданий функцією. Чи варто використовувати setStateз функцією оновлення всередині коду зворотного дзвінка AJAX, навіть якщо я знаю, що він не пакетний? Не могли б ви уточнити використання setStateзворотного дзвінка AJAX з використанням або без використання функції оновлення? Дякую!
— tonix

@Michal, привіт Міхал просто хотів задати просте запитання, чи правда, що якщо у нас є this.setState ({value: 0}); this.setState ({значення: this.state.value + 1}); перший setState буде ігнорований, і буде виконаний лише другий setState?
— Діккенс

@Dickens Я вважаю, що обидва setStateбудуть страчені, але останній виграє.
— Міхал

3

Кілька дзвінків під час одного і того ж циклу можуть бути зібрані разом. Наприклад, якщо ви намагаєтеся збільшити кількість предметів більше одного разу в тому ж циклі, це призведе до еквіваленту:

Object.assign(
  previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)

https://reactjs.org/docs/react-component.html


3

як у док

setState () запитує зміни до стану компонента та повідомляє React, що цей компонент та його діти потрібно повторно відредагувати з оновленим станом. Це основний метод, який ви використовуєте для оновлення інтерфейсу користувача у відповідь на обробники подій та відповіді сервера.

це буде попередньо змінити, як у черзі ( FIFO : First In First Out), перший виклик буде першим, щоб передформувати


привіт Алі, я просто хотів задати просте запитання, чи правда, що якщо у нас є this.setState ({value: 0}); this.setState ({значення: this.state.value + 1}); перший setState буде ігнорований, і буде виконаний лише другий setState?
— Діккенс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.