Я знаю, что React может выполнять обновления состояния асинхронно и в пакетном режиме для оптимизации производительности. Поэтому вы никогда не можете доверять обновлению состояния после вызова setState
. Но можете ли вы доверять React, чтобы обновлять состояние в том же порядке, что setState
и для
- тот же компонент?
- разные компоненты?
Рассмотрите возможность нажатия кнопки в следующих примерах:
1. Существует ли вероятность, что a ложно, а 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. Существует ли вероятность, что a ложно, а 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 выполняет эти обновления состояния, и ничего больше.
Мы очень ценим любой ответ, подкрепленный документацией.
источник
Ответы:
Я работаю над React.
TLDR:
Да.
Да.
Порядок обновлений всегда соблюдается. Видите ли вы промежуточное состояние «между ними» или нет, зависит от того, находитесь вы внутри партии или нет.
В настоящее время (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()
будет обрабатываться немедленно, как это происходит. В этом случае, да, вы бы видеть промежуточное состояние:Мы понимаем, что неудобно, что поведение зависит от того, работаете вы в обработчике событий или нет . Это изменится в будущей версии React, которая будет пакетировать все обновления по умолчанию (и предоставлять дополнительный API для синхронного сброса изменений). Пока мы не переключим поведение по умолчанию (возможно, в React 17), есть API, который вы можете использовать для принудительной пакетной обработки :
Все внутренние обработчики событий React обернуты
unstable_batchedUpdates
, поэтому по умолчанию они сгруппированы. Обратите внимание, что двойная упаковка обновления неunstable_batchedUpdates
имеет никакого эффекта. Обновления сбрасываются, когда мы выходим из самого внешнегоunstable_batchedUpdates
вызова.Этот API «нестабилен» в том смысле, что мы удалим его, когда пакетная обработка уже включена по умолчанию. Однако мы не будем удалять его в младшей версии, поэтому вы можете смело полагаться на него до React 17, если вам нужно принудительно использовать пакетную обработку в некоторых случаях за пределами обработчиков событий React.
Подводя итог, это сбивает с толку, потому что React по умолчанию выполняет пакетные операции только внутри обработчиков событий. Это изменится в будущих версиях, и тогда поведение будет более простым. Но решение состоит не в том, чтобы пакетировать меньше , а по умолчанию - больше . Вот что мы собираемся делать.
источник
obj.a = true; obj.b = true
), а затем в конце просто сделатьthis.setState(obj)
. Это безопасно независимо от того, находитесь ли вы внутри обработчика событий или нет. Это может быть изящный трюк, если вы часто делаете ошибку, задавая состояние несколько раз вне обработчиков событий.componentDidUpdate
и другие обратные вызовы жизненного цикла, начиная с React 16? Заранее спасибо!На самом деле это довольно интересный вопрос, но ответ не должен быть слишком сложным. Есть отличная статья о среде, в которой есть ответ.
1) Если вы сделаете это
Не думаю, что будет ситуация, когда
a
будетtrue
иb
будетfalse
из-за пакетирования .Однако, если
b
зависит от,a
то действительно может возникнуть ситуация, когда вы не получите ожидаемое состояние.После обработки всех вышеуказанных вызовов
this.state.value
будет 1, а не 3, как вы ожидали.Об этом говорится в статье:
setState accepts a function as its parameter
Это даст нам
this.state.value === 3
источник
this.state.value
обновляется и обработчики событий (гдеsetState
дозируется) и AJAX обратных вызовов (гдеsetState
это не дозируется). В обработчиках событий я бы использовал,updater function
чтобы всегда быть уверенным, что я обновляю состояние, используя текущее обновленное состояние, предоставляемое функцией. Должен ли я использоватьsetState
функцию обновления внутри кода обратного вызова AJAX, даже если я знаю, что он не пакетируется? Не могли бы вы прояснить использованиеsetState
в обратном вызове AJAX с или без использования функции обновления? Спасибо!setState
будут казнены, но победит последний.Несколько вызовов в течение одного цикла могут быть объединены в пакет. Например, если вы попытаетесь увеличить количество товара более одного раза в одном и том же цикле, это приведет к эквиваленту:
https://reactjs.org/docs/react-component.html
источник
как в документе
он выполнит изменение, как в очереди ( FIFO : First In First Out), первый вызов будет первым для предварительной обработки
источник