182 votes

React conserve-t-il l'ordre des mises à jour d'état?

Je sais que Réagir peut effectuer des mises à jour d'état de manière asynchrone et dans le lot pour l'optimisation des performances. Par conséquent, vous ne pouvez jamais faire confiance à l'état pour être mis à jour après les avoir appelés setState. Mais pouvez-vous faire confiance à Réagir à la mise à jour de l'état dans le même ordre que setState est appelé pour

  1. le même composant?
  2. les différents composants?

Envisager cliquant sur le bouton dans les exemples suivants:

1. Est-il une possibilité que a est faux et b est vraie pour:

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. Est-il une possibilité que a est faux et b est vraie pour:

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 });
  }
}

Gardez à l'esprit que ce sont des simplifications extrêmes de mon cas d'utilisation. Je me rends compte que je peux le faire différemment, par exemple en mettant à jour l'état params dans le même temps, dans l'exemple 1, ainsi que l'exécution de la deuxième mise à jour d'état dans un rappel de la première mise à jour d'état dans l'exemple 2. Cependant, ce n'est pas ma question, et je suis seulement intéressé si il y a une façon de Réagir effectue les mises à jour d'état, rien d'autre.

Aucune réponse soutenue par la documentation est grandement apprécié.

432voto

Dan Points 16670

Je travaille sur de Réagir.

TLDR:

Mais pouvez-vous faire confiance à Réagir à la mise à jour de l'état dans le même ordre que setState est appelé pour

  • le même composant?

Oui.

  • les différents composants?

Oui.

L' ordre des mises à jour est toujours respecté. Si vous voyez un état intermédiaire "entre" eux ou pas dépend de si vous êtes à l'intérieur d'un lot ou pas.

Actuellement (réaction de 16 et plus tôt), seules les mises à jour à l'intérieur de Réagir gestionnaires d'événements sont groupées par défaut. Il y a une instabilité de l'API à force de traitement par lots à l'extérieur de gestionnaires d'événements pour les rares cas où vous en avez besoin.

Dans les futures versions (probablement Réagir 17 et plus tard), de Réagir lot toutes les mises à jour par défaut, de sorte que vous n'aurez pas à penser à ce sujet. Comme toujours, nous allons annoncer tout changement à ce sujet sur le Réagir blog et dans les notes de version.


La clé pour comprendre c'est que peu importe combien de setState() des appels dans la façon dont de nombreux composants dont vous n'avez à l'intérieur d'une Réagir gestionnaire d'événements, ils ne produisent qu'une seule rendre à nouveau à la fin de l'événement. Ceci est crucial pour une bonne performance dans les applications de grande taille, car si Child et Parent chaque appel setState() lors de la manipulation d'un événement de clic, vous ne voulez pas de re-rendre l' Child deux fois.

Dans les deux exemples, setState() des appels se produisent à l'intérieur d'une Réagir gestionnaire d'événement. Par conséquent, ils sont toujours vidée à la fin de l'événement (et vous ne voyez pas l'état intermédiaire).

Les mises à jour sont toujours faiblement fusionné dans l'ordre où ils se produisent. Donc, si la première mise à jour est - {a: 10}, le second est {b: 20}, et la troisième est {a: 30}, le rendu de l'état sera {a: 30, b: 20}. La plus récente mise à jour de l'état de clés (par exemple, a dans mon exemple) toujours "gagne".

L' this.state objet est mis à jour lorsque nous nous ré-rendu de l'INTERFACE utilisateur à la fin du lot. Donc, si vous avez besoin de mettre à jour l'état basé sur un état antérieur (tels que l'incrémentation d'un compteur de), vous devez utiliser la fonctionnelle setState(fn) version qui vous donne l'état précédent, au lieu de lire à partir d' this.state. Si vous êtes curieux de connaître le raisonnement pour cela, je vous l'ai expliqué en profondeur dans ce commentaire.


Dans votre exemple, de ne pas nous voir "l'état intermédiaire" parce que nous sommes à l'intérieur d'une Réagir gestionnaire d'événements où le dosage est activé (en raison de Réagir "sait" quand nous sommes sortie de cet événement).

Toutefois, les deux à Réagir 16 et versions antérieures, il n'existe pas encore de traitement par lots, par défaut, à l'extérieur de Réagir gestionnaires d'événements. Alors, si dans votre exemple, nous avons eu une réponse AJAX gestionnaire au lieu de handleClickchaque setState() seraient traités immédiatement, comme il arrive. Dans ce cas, oui, vous permettrait de voir un état intermédiaire:

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
});

Nous nous rendons compte qu'il est gênant que le comportement est différent selon que vous êtes dans un gestionnaire d'événement ou pas. Cela va changer dans un avenir Réagir version qui sera le lot de toutes les mises à jour par défaut (et de fournir un opt-in API pour vider les changements de manière synchrone). Jusqu'à ce que nous changer le comportement par défaut (peut-être Réagir 17), il existe une API que vous pouvez utiliser pour forcer le traitement par lots:

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
});

En interne Réagir les gestionnaires d'événements sont en train d'être enveloppé dans unstable_batchedUpdates qui est pourquoi elles sont groupées par défaut. Notez que l'enchaînement d'un jour, en unstable_batchedUpdates deux fois n'a pas d'effet. Les mises à jour sont vidés quand nous sortons de la ultrapériphériques unstable_batchedUpdates appel.

Cette API est "instable" dans le sens que nous allons en retirer quand le dosage est déjà activé par défaut. Cependant, nous n'avons pas le supprimer dans une version mineure, vous pouvez compter sur elle jusqu'à ce que Réagir 17 si vous avez besoin de la force de dosage, dans certains cas, à l'extérieur de Réagir gestionnaires d'événements.


Pour résumer, c'est un sujet à confusion en raison de Réagir qu'à des lots à l'intérieur des gestionnaires d'événement par défaut. Cela va changer dans les futures versions, et le comportement sera plus simple ensuite. Mais la solution n'est pas de lot moins, c'est le lot de plus par défaut. C'est ce que nous allons faire.

9voto

Michal Points 2224

C'est en fait une très intéressante question, mais la réponse ne devrait pas être trop compliqué. Il y a ce grand article sur un milieu qui a une réponse.

1) Si vous faites cela,

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

Je ne pense pas qu'il y aura une situation où l' a sera true et b sera false en raison de dosage.

Toutefois, si b dépend a puis il y a en effet peut-être une situation où vous ne seriez pas obtenir l'état attendu.

// 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});

Après tous les appels sont traités this.state.value sera 1, pas 3 comme vous l'attendez.

Cela est mentionné dans l'article: 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}));

Cela nous donnera this.state.value === 3

4voto

Mosè Raguzzini Points 4029

Plusieurs appels au cours du même cycle peuvent être mis en lot. Par exemple, si vous essayez d’incrémenter une quantité d’article plus d’une fois dans le même cycle, l’équivalent de:

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

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

3voto

Ali Points 5549

comme dans la doc

setState() place en file d'attente des modifications à l'état des composants et dit à Réagir que ce composant et ses enfants ont besoin d'être ré-rendu avec la mise à jour de l'état. C'est la première méthode que vous utilisez pour mettre à jour l'utilisateur interface en réponse à des gestionnaires d'événements et les réponses du serveur.

il préformation le changement, comme dans la file d'attente (FIFO : First In First Out) le premier appel sera d'abord de la préforme

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X