Redux: mapStateToProps () n'est pas appelée

Créé le 13 mai 2016  ·  4Commentaires  ·  Source: reduxjs/redux

J'ai lu des tonnes de documentation - sur tous les sites populaires - mais je n'ai trouvé nulle part de réponse. Je demande ici pour voir si j'ai manqué de la documentation ou un élément essentiel de compréhension.

Le problème que je vois est que la fonction mapStateToProps () de mon composant n'est appelée qu'une seule fois (à l'initialisation). Il n'est plus jamais appelé, même lorsque le réducteur est appelé. Je commence à penser que c'est parce que je ne comprends pas comment les actions / réducteurs / accessoires sont tous liés entre eux.

Laissez-moi d'abord dire ce que je sais:

  • Je comprends que les actions décrivent les mises à jour d'une partie (une propriété) de ce magasin.
  • Je comprends que chaque réducteur est responsable de la mise à jour de sa tranche / partie du magasin. (Et je comprends vraiment que le réducteur doit renvoyer un nouvel objet pour sa tranche.)
  • Je comprends que mapStateToProps "récupère" certaines données du magasin et les transmet comme accessoires au composant nommé.

Mais ... je n'ai pas trouvé de description de la relation entre les actions, les réducteurs et les mapStateToProps. Mes questions:

  • Je considère le magasin comme un objet, avec des propriétés qui contiennent des données sur l'état de l'application. Ces propriétés de niveau supérieur peuvent être des objets qui fournissent une sous-arborescence pour contenir des informations sur une partie de l'application. Est-ce vrai?
  • Lorsqu'une action est envoyée au magasin, quelle propriété (ou sous-arborescence du magasin) est mise à jour? Il semble qu'il y ait une création / association automatique à une propriété dans le magasin, mais comment cela est-il déterminé?
  • Comment mapStateToProps "sait" écouter les mises à jour de la propriété / partie appropriée du magasin? Qu'est-ce qui les unit?
  • Comment pourrais-je déboguer cela? Que dois-je faire pour comprendre pourquoi la distribution de l'action ne semble pas déclencher mapStateToProps?
  • Ou est-ce que je pense à tout cela mal ...?
question

Commentaire le plus utile

Avez-vous parcouru http://redux.js.org/docs/Troubleshooting.html et vérifié que votre cas ne fait pas partie des problèmes décrits?

Je considère le magasin comme un objet, avec des propriétés qui contiennent des données sur l'état de l'application. Ces propriétés de niveau supérieur peuvent être des objets qui fournissent une sous-arborescence pour contenir des informations sur une partie de l'application. Est-ce vrai?

Oui. Store contient l'objet d'état de niveau supérieur en interne. Il permet d'accéder à cet objet à partir de store.getState() . Cet objet correspond à tout ce que vous retournez du réducteur de racine, et il ressemble souvent à un arbre.

Lorsqu'une action est envoyée au magasin, quelle propriété (ou sous-arborescence du magasin) est mise à jour? Il semble qu'il y ait une création / association automatique à une propriété dans le magasin, mais comment cela est-il déterminé?

Non, il n'y a pas de création automatique de quoi que ce soit. Le magasin commence juste à pointer vers le résultat de l'appel de votre réducteur racine.

Si votre réducteur de racine est une fonction que vous avez écrite vous-même, son résultat est ce que vous obtiendrez après avoir distribué une action:

function counter(state = 0, action) {
  if (action.type === 'INCREMENT') {
    return state + 1; // will become the next value of store.getState() after store.dispatch()
  }
  return state;
}

Si vous _générez_ le réducteur de racine avec quelque chose comme combineReducers({ foo, bar }) , c'est la même chose que si vous aviez écrit un réducteur de racine à la main qui ressemble à ceci:

function root(state = {}, action) {
  return {
    foo: foo(state.foo, action),
    bar: bar(state.bar, action)
  }
}

C'est de là que proviennent souvent les propriétés de l'État. Leurs valeurs sont déterminées par ce que vos réducteurs foo et bar renvoyés lors de la gestion de l'action, respectivement, donc encore une fois, vous avez un contrôle total sur eux.

Comment mapStateToProps "sait" écouter les mises à jour de la propriété / partie appropriée du magasin? Qu'est-ce qui les unit?

Vous passez la fonction mapStateToProps à connect() . Il génère un composant qui utilise store.subscribe() pour écouter chaque modification apportée au magasin. Lorsque le magasin a changé, le composant généré lit store.getState() et le passe à mapStateToProps() pour déterminer les prochains accessoires à transmettre. S'ils ont changé, le rendu de votre composant sera refait avec eux.

Comment pourrais-je déboguer cela? Que dois-je faire pour comprendre pourquoi la distribution de l'action ne semble pas déclencher mapStateToProps?

Ajoutez un middleware comme https://github.com/theaqua/redux-logger. Vérifiez que l'état se met à jour après l'action comme vous le souhaitez. Si mapStateToProps n'est même pas appelé, cela signifie que

  • Soit vous avez oublié de dispatch() une action et vous venez d'appeler un créateur d'action,
  • Ou votre réducteur a renvoyé une valeur référentielle identique, donc connect() n'a pas pris la peine d'appeler mapStateToProps() .

Ces deux cas sont décrits dans http://redux.js.org/docs/Troubleshooting.html.

À l'avenir, nous vous demandons d'essayer d'abord de poser des questions sur StackOverflow et d'utiliser le suivi des problèmes lorsque vous êtes sûr qu'il y a un bogue dans la bibliothèque que vous pouvez reproduire de manière cohérente avec un exemple que vous pouvez partager.

Merci!

Tous les 4 commentaires

Avez-vous parcouru http://redux.js.org/docs/Troubleshooting.html et vérifié que votre cas ne fait pas partie des problèmes décrits?

Je considère le magasin comme un objet, avec des propriétés qui contiennent des données sur l'état de l'application. Ces propriétés de niveau supérieur peuvent être des objets qui fournissent une sous-arborescence pour contenir des informations sur une partie de l'application. Est-ce vrai?

Oui. Store contient l'objet d'état de niveau supérieur en interne. Il permet d'accéder à cet objet à partir de store.getState() . Cet objet correspond à tout ce que vous retournez du réducteur de racine, et il ressemble souvent à un arbre.

Lorsqu'une action est envoyée au magasin, quelle propriété (ou sous-arborescence du magasin) est mise à jour? Il semble qu'il y ait une création / association automatique à une propriété dans le magasin, mais comment cela est-il déterminé?

Non, il n'y a pas de création automatique de quoi que ce soit. Le magasin commence juste à pointer vers le résultat de l'appel de votre réducteur racine.

Si votre réducteur de racine est une fonction que vous avez écrite vous-même, son résultat est ce que vous obtiendrez après avoir distribué une action:

function counter(state = 0, action) {
  if (action.type === 'INCREMENT') {
    return state + 1; // will become the next value of store.getState() after store.dispatch()
  }
  return state;
}

Si vous _générez_ le réducteur de racine avec quelque chose comme combineReducers({ foo, bar }) , c'est la même chose que si vous aviez écrit un réducteur de racine à la main qui ressemble à ceci:

function root(state = {}, action) {
  return {
    foo: foo(state.foo, action),
    bar: bar(state.bar, action)
  }
}

C'est de là que proviennent souvent les propriétés de l'État. Leurs valeurs sont déterminées par ce que vos réducteurs foo et bar renvoyés lors de la gestion de l'action, respectivement, donc encore une fois, vous avez un contrôle total sur eux.

Comment mapStateToProps "sait" écouter les mises à jour de la propriété / partie appropriée du magasin? Qu'est-ce qui les unit?

Vous passez la fonction mapStateToProps à connect() . Il génère un composant qui utilise store.subscribe() pour écouter chaque modification apportée au magasin. Lorsque le magasin a changé, le composant généré lit store.getState() et le passe à mapStateToProps() pour déterminer les prochains accessoires à transmettre. S'ils ont changé, le rendu de votre composant sera refait avec eux.

Comment pourrais-je déboguer cela? Que dois-je faire pour comprendre pourquoi la distribution de l'action ne semble pas déclencher mapStateToProps?

Ajoutez un middleware comme https://github.com/theaqua/redux-logger. Vérifiez que l'état se met à jour après l'action comme vous le souhaitez. Si mapStateToProps n'est même pas appelé, cela signifie que

  • Soit vous avez oublié de dispatch() une action et vous venez d'appeler un créateur d'action,
  • Ou votre réducteur a renvoyé une valeur référentielle identique, donc connect() n'a pas pris la peine d'appeler mapStateToProps() .

Ces deux cas sont décrits dans http://redux.js.org/docs/Troubleshooting.html.

À l'avenir, nous vous demandons d'essayer d'abord de poser des questions sur StackOverflow et d'utiliser le suivi des problèmes lorsque vous êtes sûr qu'il y a un bogue dans la bibliothèque que vous pouvez reproduire de manière cohérente avec un exemple que vous pouvez partager.

Merci!

Répondre dans l'ordre:

  • Redux suppose généralement que la partie supérieure de votre arborescence d'états est un simple objet Javascript (bien qu'il soit également courant d'utiliser les types de données fournis par la bibliothèque Immutable.js). Ce que vous mettez à l'intérieur de cet objet et comment vous structurez le contenu dépend entièrement de vous. L'approche la plus courante consiste à organiser les choses par domaine. Par exemple, une arborescence d'états pour une application de blog hypothétique peut ressembler à: { users : {}, posts : {}, comments : {}, ui : {} } . Voir http://redux.js.org/docs/FAQ.html#reducers -share-state et http://redux.js.org/docs/FAQ.html#organizing -state-nested-data.
  • Techniquement, lorsque vous créez votre boutique, vous définissez une fonction "réducteur" unique. Cette fonction est appelée avec l'état actuel et l'action entrante, et est responsable du retour d'un nouvel objet d'état, avec _toutes_ les mises à jour appropriées, effectuées de manière immuable (c'est-à-dire, pas de modifications directes sur place - tout ce qui doit être mis à jour doit être fait en faisant des copies, en les modifiant et en renvoyant les copies à la place, et la mise à jour d'un champ imbriqué signifie également copier et mettre à jour tous ses ancêtres). Cependant, comme mettre chaque dernier bit de logique de mise à jour dans une fonction géante est une mauvaise idée, cette logique de mise à jour est presque toujours divisée en fonctions de sous-réducteur réutilisables plus petites. La façon dont vous structurez cette logique dépend à nouveau de vous, mais rien n'est mis à jour automatiquement - c'est à votre logique de réduction de le faire. L'utilitaire combineReducers fourni avec Redux facilite la création d'une fonction de réduction spécifique chargée de gérer les mises à jour d'une tranche de données donnée, comme: const combinedReducer = combineReducers({users : userReducer, posts : postReducer, comments : commentsReducer, ui : uiReducer});
  • Redux avertit simplement tous les abonnés chaque fois qu'une action _any_ est exécutée dans le magasin. La fonction React Redux connect() s'abonne, puis appelle le composant mapStateToProps pour voir si l'une des données extraites a changé depuis la dernière fois. Il n'y a pas d'abonnement d'état imbriqué spécifique. Voir http://redux.js.org/docs/FAQ.html#store -setup-subscriptions.
  • La principale cause de non mise à jour d'un composant est la mutation directe de l'état dans votre réducteur. Voir http://redux.js.org/docs/FAQ.html#react -not-rerendering. Au-delà de cela, il existe un certain nombre d'addons que vous pouvez utiliser pour déboguer et enregistrer des actions. J'en ai un certain nombre sur https://github.com/markerikson/redux-ecosystem-links/blob/master/devtools.md.

J'espère que cela aidera à clarifier les choses.

Au-delà de cela, comme l'a dit Dan: les questions d'utilisation sont généralement mieux adaptées à Stack Overflow. De plus, la communauté Reactiflux sur Discord dispose de nombreux canaux de discussion dédiés aux technologies liées à React, y compris Redux, et il y a toujours un certain nombre de personnes qui traînent pour répondre aux questions. Il y a un lien d'invitation sur https://www.reactiflux.com .

@ richb-hanover Vous avez probablement été témoin de la comparaison superficielle qui est effectuée par la connexion react-redux. Cela signifie que les objets imbriqués dans d'autres objets ne donneront pas lieu à un rendu même s'ils sont modifiés car seule la clé racine est vérifiée pour les modifications.

http://redux.js.org/docs/faq/ReactRedux.html#why -isnt-my-component-re-rendering-or-my-mapstatetoprops-running

@ richb-hanover Dans mon cas, @gaearon a détecté le problème:

Ou votre réducteur a renvoyé une valeur référentielle identique, donc connect () n'a pas pris la peine d'appeler mapStateToProps ().

J'utilisais ce correctif et c'était le code résultant

const mapStateToProps = (state, props) => {
    return { id: props.id, currentState: state[props.id] };
}

const mapDispatchToProps = (dispatch) => {
    return {
        increment: (id) => {
            dispatch({ type: 'INCREMENT', id: id })
        }
    }
}

const DumbListItem = ({
    increment,
    id,
    currentState
}) => (
        <div>
            <li onClick={() => increment()}>{id}</li>
            <span>{currentState}</span>
        </div>
    );

export const ConnectedListItem = connect(
    mapStateToProps,
    mapDispatchToProps
)(DumbListItem);

Comme je n'ai pas passé le paramètre id à l'appel d'incrément (soit en tant que increment(id) dans le modèle, soit via le deuxième paramètre de mapDispatchToProps ), le magasin n'a pas mise à jour correctement, vérifiez si c'est peut-être votre problème.

Cette page vous a été utile?
0 / 5 - 0 notes