web-dev-qa-db-fra.com

Comprendre React-Redux et mapStateToProps ()

J'essaie de comprendre la méthode connect de react-redux et ses fonctions en tant que paramètres. En particulier mapStateToProps().

Si je comprends bien, la valeur de retour de mapStateToProps sera un objet dérivé de state (tel qu'il réside dans le magasin), dont les clés seront transmises à votre composant cible (le composant auquel la connexion est appliquée).

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 que ça va?
Q: Est-ce attendu?
Q: Est-ce un anti-modèle?

169

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?
A: Non, ce n'est pas un anti-modèle.

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 pendant un temps supplémentaire, ce qui augmente la modularité de votre code. Cela vous permet également de simplifier l'état de vos composants en tant que sous-ensemble de l'état de votre application, ce qui vous aide en réalité à vous conformer au modèle Redux. 

Pensez-y de cette façon: un magasin est supposé contenir l’état entier de votre application.
Pour les applications volumineuses, cela peut contenir des dizaines de propriétés imbriquées dans plusieurs couches.
Vous ne voulez pas transporter tout cela autour à chaque appel (cher). 

Sans mapStateToProps ou un analogue de celui-ci, vous seriez tenté de scinder votre état d'un autre moyen d'améliorer les performances/de simplifier.

32
Richard Strickland

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

Imaginez que vous ayez une clé posts dans votre application state.posts 

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

Et composant Posts 

Par défaut, connect()(Posts) rendra tous les accessoires d'état 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 bindActionCreators vous pouvez le faire aussi plus facilement comme

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

Maintenant, vous pouvez 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 d'actionCreator: deletePost

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

Donc, bindActionCreators prendra juste vos actions, encapsulez-les dans un appel dispatch. (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;
  }, {})
}
106
webdeb

Vous avez la première partie à droite: 

Oui mapStateToProps a l'état du magasin en tant qu'argument/paramètre (fourni par react-redux::connect) et est utilisé pour lier le composant à une partie de l'état du magasin.

En liant, je veux dire que l'objet retourné par mapStateToProps sera fourni au moment de la construction sous forme d'accessoires, et toute modification ultérieure 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 à rendre les choses plus claires:

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 réagissant appelé itemsFilters qui gère l’affichage et maintient l’état du filtre dans l’état Redux Store, le composant Demo écoute ou est abonné aux filtres de l’état Redux Store de manière à ce que l’état des filtres change (avec l’aide de filtersComponent ) react-redux détecte une modification et notifie ou "publie" tous les composants écoutants/abonnés en envoyant les modifications à leur componentWillReceiveProps qui, dans cet exemple, va déclencher un nouveau filtrage des éléments et actualiser l'affichage en raison du fait que l'état a changé.

Faites-moi savoir si l'exemple est source de 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 de Redux Store!

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

L'état Redux Store est une combinaison d'états réducteurs Redux, chacun étant responsable de la gestion d'une petite partie de la logique d'application. Ces attributs de réducteurs sont accessibles à l’aide de react-redux::connect@mapStateToProps par n’importe quel composant! Ce qui rend l'application accessible à l'état de magasin Redux à l'échelle alors que l'état de composant est exclusif à lui-même.

31
Mohamed Mellouki

Cet exemple react & redux est basé sur l'exemple de Mohamed Mellouki . Mais valide l'utilisation des règles prettify et linting . Notez que nous définissons nos méthodes props Et dispatch en utilisant PropTypes afin que notre compilateur ne nous crie pas dessus . Cet exemple incluait également certaines lignes de code manquantes dans Mohamed Exemple. Pour utiliser connect, vous devez l'importer à partir de react-redux . Cet exemple Associe également binds à la méthode filterItems afin d'éviter les problèmes de scope dans Le composant . Ce code source a été automatiquement mis en forme à 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 pour un lieu de départ pour votre composant.

5
Patrick W. McMahon

React-Reduxconnect 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. 

2
ArunValaven

bolerplate pour décrire mapstateToProps: 

// Ceci est une implémentation grandement simplifiée de ce qu'un conteneur Redux ferait

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} />;
    }
  }
}
0
zloctb