Comprendre React-Redux et mapStateToProps ()

219

J'essaie de comprendre la méthode de connexion de react-redux et les fonctions qu'elle prend comme paramètres. En particulier mapStateToProps().

D'après ce que je comprends, la valeur de retour de mapStateToPropssera un objet dérivé de state (tel qu'il vit dans le magasin), dont les clés seront transmises à votre composant cible (le composant connect est appliqué à) comme accessoires.

Cela signifie que l'état tel qu'il est consommé par votre composant cible peut avoir une structure très différente de l'état tel qu'il est stocké dans votre magasin.

Q: est-ce OK?
Q: Est-ce prévu?
Q: Est-ce un anti-modèle?

Pablo Barría Urenda
la source
11
Je ne veux pas ajouter une autre réponse au mélange ... mais je me rends compte que personne ne répond réellement à votre question ... à mon avis, ce n'est PAS un anti-modèle. La clé se trouve dans le nom mapStateTo Props que vous transmettez des propriétés en lecture seule pour un composant à consommer. J'utilise souvent mes composants de conteneur pour prendre l'état et le changer avant de le passer au composant de présentation.
Matthew Brent
3
De cette façon, mon composant de présentation est beaucoup plus simple ... Je pourrais rendre this.props.someDataplutôt que this.props.someKey[someOtherKey].someData... avoir du sens?
Matthew Brent
3
Ce tutoriel l'explique assez bien: learn.co/lessons/map-state-to-props-readme
Ayan
Salut Pablo, veuillez reconsidérer la réponse que vous avez choisie.
vsync
Revoyez comment?
Pablo Barría Urenda

Réponses:

56

Q: Is this ok?
A: oui

Q: Is this expected?
Oui, cela est prévu (si vous utilisez react-redux).

Q: Is this an anti-pattern?
R: Non, ce n'est pas un anti-motif.

Cela s'appelle «connecter» votre composant ou «le rendre intelligent». C'est par conception.

Il vous permet de découpler votre composant de votre état un temps supplémentaire ce qui augmente la modularité de votre code. Il vous permet également de simplifier l'état de vos composants en tant que sous-ensemble de l'état de votre application, ce qui, en fait, vous aide à vous conformer au modèle Redux.

Pensez-y de cette façon: un magasin est censé contenir la totalité de l' état de votre application.
Pour les grandes applications, cela pourrait contenir des dizaines de propriétés imbriquées sur plusieurs couches en profondeur.
Vous ne voulez pas transporter tout cela à chaque appel (cher).

Sans mapStateToPropsou un analogue de celui-ci, vous seriez tenté de découper votre état d'une autre manière pour améliorer les performances / simplifier.

Richard Strickland
la source
6
Je ne pense pas que donner à chaque composant un accès à l'ensemble du magasin, aussi grand soit-il, n'a rien à voir avec les performances. passer des objets ne prend pas de mémoire car c'est toujours le même objet. La seule raison d'apporter à un composant les pièces dont il a besoin est probablement 2 raisons: (1) -un accès plus facile en profondeur (2) -Évitez les bugs où un composant pourrait gâcher un état qui ne lui appartient pas
vsync
@vsync Pourriez-vous s'il vous plaît expliquer comment cela permet un accès en profondeur plus facile? Voulez-vous dire que les accessoires locaux peuvent maintenant être utilisés au lieu d'avoir à se référer à l'état global et donc plus lisibles?
Siddhartha
De plus, comment un composant pourrait-il gâcher un état qui ne lui appartient pas lorsque l'état est passé comme immuable?
Siddhartha
si l'état est immuable, je suppose que c'est bien, mais comme bonne pratique, il est préférable d'exposer aux composants uniquement les parties qui les concernent. Cela aide également les autres développeurs à mieux comprendre quelles parties (de l' objet d' état ) sont pertinentes pour ce composant. En ce qui concerne "un accès plus facile", il est plus facile en un sens que le chemin vers un état profond soit directement transmis au composant en tant qu'accessoire, et ce composant est aveugle au fait qu'il y a Redux dans les coulisses. Les composants ne devraient pas se soucier du système de gestion d'état utilisé, et ils devraient fonctionner uniquement avec les accessoires qu'ils reçoivent.
vsync
119

Oui c'est correct. C'est juste une fonction d'aide pour avoir un moyen plus simple d'accéder à vos propriétés d'état

Imaginez que vous avez une postsclé dans votre applicationstate.posts

state.posts //
/*    
{
  currentPostId: "",
  isFetching: false,
  allPosts: {}
}
*/

Et composant Posts

Par défaut connect()(Posts), tous les accessoires d'état seront disponibles pour le composant connecté

const Posts = ({posts}) => (
  <div>
    {/* access posts.isFetching, access posts.allPosts */}
  </div> 
)

Maintenant, lorsque vous mappez le state.postsà votre composant, cela devient un peu plus agréable

const Posts = ({isFetching, allPosts}) => (
  <div>
    {/* access isFetching, allPosts directly */}
  </div> 
)

connect(
  state => state.posts
)(Posts)

mapDispatchToProps

normalement vous devez écrire dispatch(anActionCreator())

avec bindActionCreatorsvous pouvez le faire aussi plus facilement comme

connect(
  state => state.posts,
  dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)
)(Posts)

Vous pouvez maintenant l'utiliser dans votre composant

const Posts = ({isFetching, allPosts, fetchPosts, deletePost }) => (
  <div>
    <button onClick={() => fetchPosts()} />Fetch posts</button>
    {/* access isFetching, allPosts directly */}
  </div> 
)

Mise à jour sur actionCreators ..

Un exemple de actionCreator: deletePost

const deletePostAction = (id) => ({
  action: 'DELETE_POST',
  payload: { id },
})

Alors, bindActionCreatorsil vous suffit de prendre vos actions, de les envelopper dans l' dispatchappel. (Je n'ai pas lu le code source de redux, mais l'implémentation pourrait ressembler à ceci:

const bindActionCreators = (actions, dispatch) => {
  return Object.keys(actions).reduce(actionsMap, actionNameInProps => {
    actionsMap[actionNameInProps] = (...args) => dispatch(actions[actionNameInProps].call(null, ...args))
    return actionsMap;
  }, {})
}
webdeb
la source
Je pense que je pourrais manquer quelque chose, mais d'où dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)viennent les actions fetchPostset les deletePostactions?
ilyo
@ilyo ce sont vos créateurs d'actions, vous devez les importer
webdeb
2
Bonne réponse! Je pense qu'il est également agréable de souligner que ce morceau de code state => state.posts(la mapStateToPropsfonction) indiquera à React quels états déclencheront un nouveau rendu du composant lorsqu'il sera mis à jour.
Miguel Péres
38

Vous avez bien compris la première partie:

Oui mapStateToPropsa l'état Store comme argument / paramètre (fourni par react-redux::connect) et son utilisé pour lier le composant avec une certaine partie de l'état store.

En liant, je veux dire que l'objet retourné par mapStateToPropssera fourni au moment de la construction comme accessoires et tout changement ultérieur sera disponible via componentWillReceiveProps.

Si vous connaissez le modèle de conception Observer, c'est exactement cela ou une petite variation de celui-ci.

Un exemple aiderait à clarifier les choses:

import React, {
    Component,
} from 'react-native';

class ItemsContainer extends Component {
    constructor(props) {
        super(props);

        this.state = {
            items: props.items, //provided by connect@mapStateToProps
            filteredItems: this.filterItems(props.items, props.filters),
        };
    }

    componentWillReceiveProps(nextProps) {
        this.setState({
            filteredItems: this.filterItems(this.state.items, nextProps.filters),
        });
    }

    filterItems = (items, filters) => { /* return filtered list */ }

    render() {
        return (
            <View>
                // display the filtered items
            </View>
        );
    }
}

module.exports = connect(
    //mapStateToProps,
    (state) => ({
        items: state.App.Items.List,
        filters: state.App.Items.Filters,
        //the State.App & state.App.Items.List/Filters are reducers used as an example.
    })
    // mapDispatchToProps,  that's another subject
)(ItemsContainer);

Il peut y avoir un autre composant React appelé itemsFiltersqui gère l'affichage et persiste l'état du filtre dans l'état Redux Store, le composant Demo "écoute" ou "est abonné" aux filtres d'état Redux Store pour que chaque fois que les filtres stockent les changements d'état (avec l'aide de filtersComponent) réagissent -redux détecte qu'il y a eu un changement et notifie ou "publie" tous les composants d'écoute / abonnés en envoyant les changements à leur componentWillReceivePropsqui, dans cet exemple, déclenchera un nouveau filtrage des éléments et rafraîchira l'affichage du fait que l'état de réaction a changé .

Faites-moi savoir si l'exemple prête à confusion ou n'est pas assez clair pour fournir une meilleure explication.

Quant à: Cela signifie que l'état tel qu'il est consommé par votre composant cible peut avoir une structure très différente de l'état tel qu'il est stocké dans votre magasin.

Je n'ai pas compris la question, mais sachez simplement que l'état de réaction ( this.setState) est totalement différent de l'état du magasin Redux!

L'état React est utilisé pour gérer le redessin et le comportement du composant React. L'état de réaction est exclusivement contenu dans le composant.

L'état du magasin Redux est une combinaison d'états de réducteurs Redux, chacun étant responsable de la gestion d'une logique d'application de petite portion. Ces attributs réducteurs sont accessibles à l'aide de react-redux::connect@mapStateToPropsn'importe quel composant! Ce qui rend l'état de la boutique Redux accessible à l'ensemble de l'application tandis que l'état des composants est exclusif à lui-même.

Mohamed Mellouki
la source
5

Cette réagir et Redux exemple est basé sur l'exemple de Mohamed Mellouki. Mais valide en utilisant des règles de raffinage et de peluchage . Notez que nous définissons nos accessoires et nos méthodes de répartition à l' aide de PropTypes afin que notre compilateur ne nous crie pas dessus . Cet exemple comprenait également quelques lignes de code qui manquaient dans l'exemple de Mohamed. Pour utiliser connect, vous devrez l'importer depuis react-redux . Cet exemple lie également la méthode filterItems, ce qui évitera des problèmes de portée dans le composant . Ce code source a été automatiquement formaté à l'aide de JavaScript Prettify .

import React, { Component } from 'react-native';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

class ItemsContainer extends Component {
  constructor(props) {
    super(props);
    const { items, filters } = props;
    this.state = {
      items,
      filteredItems: filterItems(items, filters),
    };
    this.filterItems = this.filterItems.bind(this);
  }

  componentWillReceiveProps(nextProps) {
    const { itmes } = this.state;
    const { filters } = nextProps;
    this.setState({ filteredItems: filterItems(items, filters) });
  }

  filterItems = (items, filters) => {
    /* return filtered list */
  };

  render() {
    return <View>/*display the filtered items */</View>;
  }
}

/*
define dispatch methods in propTypes so that they are validated.
*/
ItemsContainer.propTypes = {
  items: PropTypes.array.isRequired,
  filters: PropTypes.array.isRequired,
  onMyAction: PropTypes.func.isRequired,
};

/*
map state to props
*/
const mapStateToProps = state => ({
  items: state.App.Items.List,
  filters: state.App.Items.Filters,
});

/*
connect dispatch to props so that you can call the methods from the active props scope.
The defined method `onMyAction` can be called in the scope of the componets props.
*/
const mapDispatchToProps = dispatch => ({
  onMyAction: value => {
    dispatch(() => console.log(`${value}`));
  },
});

/* clean way of setting up the connect. */
export default connect(mapStateToProps, mapDispatchToProps)(ItemsContainer);

Cet exemple de code est un bon modèle de point de départ pour votre composant.

Patrick W. McMahon
la source
2

React-Redux connect est utilisé pour mettre à jour le magasin pour chaque action.

import { connect } from 'react-redux';

const AppContainer = connect(  
  mapStateToProps,
  mapDispatchToProps
)(App);

export default AppContainer;

C'est très simplement et clairement expliqué dans ce blog .

Vous pouvez cloner un projet github ou copier-coller le code de ce blog pour comprendre la connexion Redux.

ArunValaven
la source
bon manuel formapStateToProps thegreatcodeadventure.com/…
zloctb
1

Voici un aperçu / passe-partout pour décrire le comportement de mapStateToProps:

(Il s'agit d'une implémentation considérablement simplifiée de ce que fait un conteneur Redux.)

class MyComponentContainer extends Component {
  mapStateToProps(state) {
    // this function is specific to this particular container
    return state.foo.bar;
  }

  render() {
    // This is how you get the current state from Redux,
    // and would be identical, no mater what mapStateToProps does
    const { state } = this.context.store.getState();

    const props = this.mapStateToProps(state);

    return <MyComponent {...this.props} {...props} />;
  }
}

et ensuite

function buildReduxContainer(ChildComponentClass, mapStateToProps) {
  return class Container extends Component {
    render() {
      const { state } = this.context.store.getState();

      const props = mapStateToProps(state);

      return <ChildComponentClass {...this.props} {...props} />;
    }
  }
}
zloctb
la source
-2
import React from 'react';
import {connect} from 'react-redux';
import Userlist from './Userlist';

class Userdetails extends React.Component{

render(){
    return(
        <div>
            <p>Name : <span>{this.props.user.name}</span></p>
            <p>ID : <span>{this.props.user.id}</span></p>
            <p>Working : <span>{this.props.user.Working}</span></p>
            <p>Age : <span>{this.props.user.age}</span></p>
        </div>
    );
 }

}

 function mapStateToProps(state){  
  return {
    user:state.activeUser  
}

}

  export default connect(mapStateToProps, null)(Userdetails);
SM Chinna
la source