104 votes

Comment un composant connecté redux sait-il quand il doit être rendu à nouveau ?

Je passe probablement à côté de quelque chose de très évident et j'aimerais m'éclaircir.

Voici ce que je comprends.
Dans un composant de réaction naïf, nous avons states & props . Mise à jour de state con setState refait le rendu de l'ensemble du composant. props sont pour la plupart en lecture seule et leur mise à jour n'a pas de sens.

Dans un composant react qui s'abonne à un magasin redux, via quelque chose comme store.subscribe(render) il est évident qu'il effectue un nouveau rendu à chaque fois que le magasin est mis à jour.

react-redux a un assistant connect() qui injecte une partie de l'arbre d'état (qui intéresse le composant) et les créateurs d'action en tant que props au composant, généralement par le biais de quelque chose comme

const TodoListComponent = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

Mais il est entendu qu'un setState est essentiel pour le TodoListComponent pour réagir au changement de l'arbre d'état de redux (re-render), je ne peux pas trouver de state o setState dans la section TodoList fichier de composants. Il se lit comme ceci :

const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

Quelqu'un peut-il m'indiquer ce qui me manque ?

P.S. Je suis l'exemple de liste de tâches fourni avec l'application paquet redondant .

135voto

markerikson Points 442

El connect génère un composant wrapper qui s'abonne à la boutique. Lorsqu'une action est envoyée, le callback du composant wrapper est notifié. Il exécute ensuite votre mapState et compare superficiellement l'objet résultat de cette fois-ci par rapport à l'objet résultat de la dernière fois (donc si vous deviez réécriture un champ du magasin redux avec la même valeur, cela ne déclencherait pas un re-rendu). Si les résultats sont différents, il transmet les résultats à votre "vrai" composant en tant que props.

Dan Abramov a écrit une grande version simplifiée de connect à ( connect.js ) qui illustre l'idée de base, bien qu'il ne montre pas le travail d'optimisation. J'ai également des liens vers un certain nombre d'articles sur les thèmes suivants Performances de Redux qui discutent de certaines idées connexes.

mise à jour

React-Redux v6.0.0 a apporté quelques changements internes majeurs à la façon dont les composants connectés reçoivent leurs données du magasin.

Dans ce cadre, j'ai rédigé un billet qui explique comment la connect et ses composants internes fonctionnent, et comment ils ont évolué au fil du temps :

Idiomatic Redux : L'histoire et l'implémentation de React-Redux

0 votes

Merci ! Êtes-vous la même personne qui m'a aidé l'autre jour à HN ? news.ycombinator.com/item?id=12307621 avec des liens utiles ? Enchanté de faire votre connaissance :)

4 votes

Yup, c'est moi :) Je passe chemin trop de temps à chercher des discussions sur Redux en ligne - Reddit, HN, SO, Medium, et divers autres endroits. Heureux de pouvoir aider ! (Aussi FYI, je recommande fortement les canaux de chat Reactiflux sur Discord. Beaucoup de gens s'y retrouvent et répondent aux questions. Je suis généralement en ligne les soirs d'été. Le lien d'invitation est à reactivelux.com .)

0 votes

En supposant que cela répond à la question, vous pouvez accepter la réponse :)

15voto

SherylHohman Points 3894

Ma réponse est un peu à côté de la plaque. Elle fait la lumière sur un problème qui m'a conduit à ce billet. Dans mon cas, il semble que l'application ne soit pas rendue à nouveau, même si elle reçoit de nouveaux accessoires.
Les développeurs de React ont répondu à cette question souvent posée en disant que si le (store) a été muté, dans 99% des cas, c'est la raison pour laquelle react n'effectuera pas un nouveau rendu. Mais rien sur le 1% restant. La mutation n'était pas le cas ici.


TLDR ;

componentWillReceiveProps est comment le state peut rester synchronisé avec le nouveau props .

Cas limite : Une fois state des mises à jour, puis l'application fait re-rendu !


Il s'avère que si votre application utilise seulement state pour afficher ses éléments, props peut être mis à jour, mais state ne le fera pas, donc pas de nouveau rendu.

J'avais state qui dépendait de props reçu de redux store . Les données dont j'avais besoin n'étaient pas encore dans le magasin, alors je les ai récupérées à partir de l'application componentDidMount comme il se doit. J'ai récupéré les props, quand mon reducer a mis à jour le store, parce que mon composant est connecté via mapStateToProps. Mais la page n'a pas été rendue, et l'état était toujours plein de chaînes vides.

Prenons l'exemple d'un utilisateur qui charge une page "modifier un article" à partir d'une url enregistrée. Vous avez accès à la page postId à partir de l'url, mais l'information n'est pas en store encore, donc tu vas le chercher. Les éléments de votre page sont des composants contrôlés. Ainsi, toutes les données que vous affichez se trouvent dans le fichier state .

En utilisant redux, les données ont été récupérées, le magasin a été mis à jour, et le composant est connect mais l'application n'a pas reflété les changements. En regardant de plus près, props ont été reçues, mais l'application n'a pas été mise à jour. state n'a pas été mis à jour.

Bien, props sera mis à jour et se propagera, mais state ne le fera pas. Vous devez dire spécifiquement state à mettre à jour.

Vous ne pouvez pas faire cela dans render() y componentDidMount a déjà terminé ses cycles.

componentWillReceiveProps est l'endroit où vous mettez à jour state qui dépendent d'une modification prop valeur.

Exemple d'utilisation :

componentWillReceiveProps(nextProps){
  if (this.props.post.category !== nextProps.post.category){
    this.setState({
      title: nextProps.post.title,
      body: nextProps.post.body,
      category: nextProps.post.category,
    })
  }      
}

Je dois rendre hommage à cet article qui m'a éclairé sur la solution que des dizaines d'autres articles, blogs et repos ont omis de mentionner. Si quelqu'un d'autre a eu du mal à trouver une réponse à ce problème manifestement obscur, la voici :

Méthodes de cycle de vie des composants ReactJs - une plongée en profondeur

componentWillReceiveProps est l'endroit où vous allez mettre à jour state pour rester en phase avec props des mises à jour.

Une fois state des mises à jour, puis en fonction de state faire re-rendu !

3 votes

De React 16.3 à partir de maintenant, vous devrait utiliser le getDerivedStateFromProps au lieu de componentWillReceiveProps . Mais en fait, vous ne devriez même pas faire tout cela comme articulé ici

0 votes

Il ne fonctionne pas, chaque fois que l'état a changé alors componentWillReceiveProps fonctionnera donc beaucoup de cas que j'essaie cela ne fonctionne pas. En particulier, pour les props complexes ou imbriqués à plusieurs niveaux, je dois assigner un composant avec un ID et déclencher un changement pour lui et mettre à jour l'état pour rendre à nouveau les nouvelles données.

0voto

Mowzer Points 5071

Cette réponse est un résumé de l'article de Brian Vaughn intitulé Vous n'avez probablement pas besoin d'un état dérivé (07 juin 2018).

La dérivation de l'état à partir d'accessoires est un anti-modèle sous toutes ses formes. Y compris l'utilisation de l'ancien componentWillReceiveProps et le plus récent getDerivedStateFromProps .

Au lieu de dériver l'état à partir d'accessoires, envisagez les solutions suivantes.

Deux recommandations de bonnes pratiques

Recommandation 1. Composant entièrement contrôlé

function EmailInput(props) {
  return <input onChange={props.onChange} value={props.email} />;
}

Recommandation 2. Composant entièrement non contrôlé avec une clé

// parent class
class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
}

// child instance
<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

Deux alternatives si, pour une raison quelconque, les recommandations ne conviennent pas à votre situation.

Alternative 1 : Réinitialiser le composant non contrôlé avec un accessoire d'identification.

class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail,
    prevPropsUserID: this.props.userID
  };

  static getDerivedStateFromProps(props, state) {
    // Any time the current user changes,
    // Reset any parts of state that are tied to that user.
    // In this simple example, that's just the email.
    if (props.userID !== state.prevPropsUserID) {
      return {
        prevPropsUserID: props.userID,
        email: props.defaultEmail
      };
    }
    return null;
  }

  // ...
}

Alternative 2 : Réinitialiser le composant non contrôlé avec une méthode d'instance

class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail
  };

  resetEmailForNewUser(newEmail) {
    this.setState({ email: newEmail });
  }

  // ...
}

-2voto

ghazaleh javaheri Points 146

Comme je le sais, la seule chose que redux fait, lors du changement d'état du magasin est d'appeler componentWillRecieveProps si votre composant dépendait de l'état muté et alors vous devriez forcer votre composant à se mettre à jour. c'est comme ça

1-store Changement d'état-2-call(componentWillRecieveProps(()=>{3-composant changement d'état})))

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