Comprensione di React-Redux e mapStateToProps ()


220

Sto cercando di capire il metodo connect di reagire-redux e le funzioni che assume come parametri. In particolare mapStateToProps().

Per come lo capisco, il valore di ritorno di mapStateToPropssarà un oggetto derivato dallo stato (come vive nel negozio), le cui chiavi verranno passate al componente di destinazione (a cui viene applicato il componente connessione) come oggetti di scena.

Ciò significa che lo stato consumato dal componente di destinazione può avere una struttura selvaggiamente diversa dallo stato in cui è archiviato nel negozio.

D: Va bene?
D: È previsto?
Q: È un anti-pattern?


11
Non voglio aggiungere un'altra risposta al mix ... ma mi rendo conto che nessuno in realtà risponde alla tua domanda ... secondo me, NON è un anti-pattern. La chiave è nel nome mapStateTo Props che stai passando proprietà di sola lettura per un componente da consumare. Utilizzerò spesso i componenti del mio contenitore per prendere lo stato e modificarlo prima di passarlo al componente di presentazione.
Matthew Brent,

3
In questo modo il mio componente di presentazione è molto più semplice ... Potrei renderlo this.props.someDatainvece di this.props.someKey[someOtherKey].someData... avere un senso?
Matthew Brent,

3
Questo tutorial lo spiega abbastanza bene: learn.co/lessons/map-state-to-props-readme
Ayan

Ciao Pablo, ti preghiamo di riconsiderare la tua risposta scelta.
vsync,

Riconsidera come?
Pablo Barría Urenda,

Risposte:


56

Q: Is this ok?
A: si

D: Is this expected?
Sì, questo è previsto (se si utilizza la reattanza-redux).

Q: Is this an anti-pattern?
A: No, questo non è un anti-pattern.

Si chiama "connessione" del componente o "rendendolo intelligente". È di progettazione.

Ti consente di separare il componente dal tuo stato un'ulteriore volta, aumentando la modularità del codice. Inoltre, consente di semplificare lo stato del componente come sottoinsieme dello stato dell'applicazione che, di fatto, consente di conformarsi al modello Redux.

Pensaci in questo modo: un negozio dovrebbe contenere l' intero stato della tua applicazione.
Per applicazioni di grandi dimensioni, potrebbe contenere dozzine di proprietà nidificate a molti livelli di profondità.
Non vuoi trascinare tutto intorno ad ogni chiamata (costoso).

Senza mapStateToPropso qualche analogo di ciò, si sarebbe tentati di ritagliare il proprio stato in un altro modo per migliorare le prestazioni / semplificare.


6
Non credo che dare a tutti i componenti l'accesso all'intero negozio, per quanto grande possa essere, ha qualcosa a che fare con le prestazioni. passare oggetti in giro non occupa memoria poiché è sempre lo stesso oggetto. L'unico motivo per cui i componenti di cui hanno bisogno sono probabilmente 2 motivi: (1) -Accesso più semplice e più semplice (2) -Evita i bug in cui un componente potrebbe rovinare lo stato che non appartiene ad esso
vsync

@vsync Potresti spiegare in che modo ciò consente un accesso più semplice e approfondito? Vuoi dire che ora gli oggetti di scena locali possono essere usati invece di dover fare riferimento allo stato globale e quindi è più leggibile?
Siddhartha,

Inoltre, come potrebbe un componente confondere lo stato che non appartiene ad esso quando lo stato viene passato come immutabile?
Siddhartha,

se lo stato è immutabile, allora suppongo che vada bene, ma comunque, come buona pratica, è meglio esporre ai componenti solo le parti rilevanti per loro. Questo aiuta anche altri sviluppatori a capire meglio quali parti ( dell'oggetto stato ) sono rilevanti per quel componente. Per quanto riguarda l '"accesso più facile", è più facile in un certo senso che il percorso verso qualche stato profondo sia passato direttamente al componente come prop, e quel componente è cieco al fatto che lì Redux sta dietro le quinte. Ai componenti non dovrebbe interessare quale sistema di gestione dello stato viene utilizzato e dovrebbero funzionare solo con i puntelli che ricevono.
vsync,

119

Si è corretto. È solo una funzione di aiuto per avere un modo più semplice di accedere alle proprietà del tuo stato

Immagina di avere una postschiave nella tua appstate.posts

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

E componente Posts

Per impostazione predefinita connect()(Posts), tutti i puntelli di stato renderanno disponibile per il componente collegato

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

Ora, quando mappi il state.poststuo componente, diventa un po 'più bello

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

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

mapDispatchToProps

normalmente devi scrivere dispatch(anActionCreator())

con bindActionCreatorste puoi farlo anche più facilmente come

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

Ora puoi usarlo nel tuo componente

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

Aggiornamento su actionCreators ..

Un esempio di actionCreator: deletePost

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

Quindi, bindActionCreatorsprenderai semplicemente le tue azioni e le avvolgerai dispatch. (Non ho letto il codice sorgente di Redux, ma l'implementazione potrebbe essere simile a questa:

const bindActionCreators = (actions, dispatch) => {
  return Object.keys(actions).reduce(actionsMap, actionNameInProps => {
    actionsMap[actionNameInProps] = (...args) => dispatch(actions[actionNameInProps].call(null, ...args))
    return actionsMap;
  }, {})
}

Penso che potrei perdere qualcosa, ma da dove dispatch => bindActionCreators({fetchPosts, deletePost}, dispatch)vengono le azioni fetchPostse deletePostpassate?
ilyo,

@ilyo questi sono i tuoi creatori di azioni, devi importarli
webdeb

2
Bella risposta! Penso che sia anche bello sottolineare che questo pezzo di codice state => state.posts(la mapStateToPropsfunzione) dirà a React quali stati attiveranno un nuovo rendering del componente una volta aggiornato.
Miguel Péres,

38

Hai ottenuto la prima parte giusta:

mapStateToPropsha lo stato Store come argomento / parametro (fornito da react-redux::connect) e viene utilizzato per collegare il componente con una determinata parte dello stato del negozio.

Con il collegamento intendo che l'oggetto restituito mapStateToPropssarà fornito in fase di costruzione come oggetti di scena e qualsiasi modifica successiva sarà disponibile attraverso componentWillReceiveProps.

Se conosci il modello di progettazione di Observer è esattamente questa o una piccola variazione di esso.

Un esempio contribuirebbe a chiarire le cose:

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

Può esserci un altro componente di reazione chiamato itemsFiltersche gestisce il display e persiste lo stato del filtro nello stato di Redux Store, il componente Demo "ascolta" o "si abbona" ​​ai filtri di stato di Redux Store, quindi ogni volta che i filtri memorizzano lo stato (con l'aiuto di filtersComponent) reagire -redux rileva la presenza di una modifica e notifica o "pubblica" tutti i componenti in ascolto / sottoscritti inviando le modifiche alle loro componentWillReceivePropsche in questo esempio attiveranno un filtro degli elementi e aggiornerà il display a causa del fatto che lo stato di reazione è cambiato .

Fammi sapere se l'esempio è confuso o non abbastanza chiaro da fornire una spiegazione migliore.

Per quanto riguarda: Ciò significa che lo stato consumato dal componente di destinazione può avere una struttura selvaggiamente diversa dallo stato in cui è archiviato nel negozio.

Non ho ricevuto la domanda, ma so solo che lo stato di reazione ( this.setState) è totalmente diverso dallo stato di Redux Store!

Lo stato di reazione viene utilizzato per gestire il ridisegno e il comportamento del componente di reazione. Lo stato di reazione è contenuto esclusivamente nel componente.

Lo stato di Redux Store è una combinazione di stati di riduttori di Redux, ciascuno dei quali è responsabile della gestione di una piccola logica dell'app. Questi attributi dei riduttori sono accessibili con l'aiuto di react-redux::connect@mapStateToPropsqualsiasi componente! Ciò rende l'app accessibile dello stato del negozio Redux ampia mentre lo stato del componente è esclusivo di se stesso.


5

Questo esempio di reazione e redux si basa sull'esempio di Mohamed Mellouki. Ma convalida usando le regole di prettify e linting . Nota che definiamo i nostri oggetti di scena e i nostri metodi di spedizione usando PropTypes in modo che il nostro compilatore non ci urli. Questo esempio includeva anche alcune righe di codice che mancavano nell'esempio di Mohamed. Per usare connect dovrai importarlo da reagire-redux . Questo esempio anche lega i filterItems metodo ciò impedirà ambito problemi nel componente . Questo codice sorgente è stato formattato automaticamente utilizzando 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);

Questo codice di esempio è un buon modello per un punto di partenza per il tuo componente.


2

React-Redux connect viene utilizzato per aggiornare l'archivio per ogni azione.

import { connect } from 'react-redux';

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

export default AppContainer;

È spiegato in modo molto semplice e chiaro in questo blog .

Puoi clonare il progetto github o copiare incollare il codice da quel blog per capire la connessione Redux.


buon manuale formapStateToProps thegreatcodeadventure.com/…
zloctb

1

Ecco un contorno / boilerplate per descrivere il comportamento di mapStateToProps :

(Questa è un'implementazione notevolmente semplificata di ciò che fa un contenitore 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} />;
  }
}

e poi

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

-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);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.