Не реагировать, следить за порядком на государственную обновления?


Я знаю, что React может выполнять обновления состояния асинхронно и в пакетном режиме для оптимизации производительности. Поэтому вы никогда не можете доверять состоянию, которое будет обновлено после вызова setState. Но можете ли вы доверять реагировать на обновить состояние в том же порядке, что и setState называется на

  1. один и тот же компонент?
  2. различные компоненты?

рассмотрите возможность нажатия кнопки в следующем примеры:

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 выполняет эти обновления состояния, и ничего больше.

любой ответ, подкрепленный документацией, очень ценится.

4 70

4 ответа:

я работаю над React.

TLDR:

но вы можете доверять реагировать на обновление состояния в том же порядке, как setState вызывается для

  • один и тот же компонент?

да.

  • различные компоненты?

да.

The ордер обновлений всегда соблюдается. Если вы видите промежуточное состояние" между " ними или нет зависит от того, находитесь ли вы внутри в партии или нет.

в настоящее время (реагировать 16 и ранее), по умолчанию пакетируются только обновления внутри обработчиков событий React. Существует нестабильный API для принудительного дозирования вне обработчиков событий для редких случаев, когда вам это нужно.

в будущих версиях (вероятно, React 17 и более поздних), React будет пакетировать все обновления по умолчанию, так что вам не придется думать об этом. Как всегда, мы будем сообщить какие-либо изменения, об этом на реагировать блог и в примечаниях к выпуску.


ключ к пониманию этого заключается в том, что независимо от того, сколько setState() вызывает сколько компонентов вы делаете внутри обработчика событий React, они будут производить только один рендеринг в конце события. Это имеет решающее значение для хорошей производительности в больших приложениях, потому что если Child и Parent каждый вызов setState() при обработке события click, вы не хотите повторно визуализировать Child два раза.

в обоих ваших примерах,setState() вызовы происходят внутри обработчика событий React. Поэтому они всегда сливаются вместе в конце события (и вы не видите промежуточного состояния).

обновления всегда неглубоко слиты в порядке их возникновения. Так что если первое обновление {a: 10}, второй -{b: 20}, третья -{a: 30}, состояние визуализации будет {a: 30, b: 20}. Чем больше недавнее обновление до того же ключа состояния (например, как a в моем примере) всегда "побеждает".

The this.state объект обновляется, когда мы повторно визуализируем пользовательский интерфейс в конце пакета. Поэтому, если вам нужно обновить состояние на основе предыдущего состояния (например, увеличить счетчик), вы должны использовать functional 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
});

внутренне реагируют обработчики событий все завернуты в unstable_batchedUpdates именно поэтому они пакуются по умолчанию. Обратите внимание, что упаковка обновления в unstable_batchedUpdates дважды не имеет никакого эффекта. Обновления сбрасываются, когда мы выходим из внешнего unstable_batchedUpdates звонок.

этот API является "нестабильным" в том смысле, что мы удалим его, когда пакетирование уже включено по умолчанию. Тем не менее, мы не будем удалять его в минорной версии, так что вы можно смело полагаться на него до React 17, Если вам нужно принудительно дозировать в некоторых случаях вне обработчиков событий React.


подводя итог, это запутанная тема, потому что по умолчанию реагируют только пакеты внутри обработчиков событий. Это изменится в будущих версиях, и тогда поведение будет более простым. Но решение не в пакета менее, это партия больше по умолчанию. Вот что мы собираемся сделать.

это на самом деле довольно интересный вопрос, но ответ не должен быть слишком сложным. Есть такая великая статья на medium такого рода ответы на вопрос.

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

а в doc

setState ()добавляет изменения компонента государственного и говорит среагировать этот компонент и его дети должны быть повторно вынесено с обновленное состояние. Это основной метод, используемый для обновления пользователя интерфейс в ответ на обработчики событий и ответы сервера.

Он будет предварительно формировать изменения, как в очереди (ФИФО: First In First Out) первый звонок будет первым преформа

несколько вызовов в течение одного цикла могут быть объединены в пакет. Например, если вы попытаетесь увеличить количество товара более одного раза в одном цикле, это приведет к эквиваленту:

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

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