Чи підтримує 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.