Che cos'è mapDispatchToProps?


358

Stavo leggendo la documentazione per la libreria Redux e ha questo esempio:

Oltre a leggere lo stato, i componenti del contenitore possono inviare azioni. In modo simile, è possibile definire una funzione chiamata mapDispatchToProps()che riceve il dispatch()metodo e restituisce i puntelli di callback che si desidera iniettare nel componente di presentazione.

Questo in realtà non ha senso. Perché hai bisogno mapDispatchToPropsquando hai già mapStateToProps?

Forniscono anche questo utile esempio di codice:

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

Qualcuno può spiegare in parole povere che cos'è questa funzione e perché è utile?

Risposte:


577

Sento che nessuna delle risposte ha chiarito il motivo per cui mapDispatchToPropsè utile.

A questo si può veramente rispondere solo nel contesto del container-componentmodello, che ho trovato meglio compreso dalla prima lettura: Componenti del contenitore e Uso con React .

In poche parole, componentsdovresti preoccuparti solo di mostrare cose. L' unico posto in cui dovrebbero ottenere informazioni sono i loro oggetti di scena .

Separato da "visualizzare elementi" (componenti) è:

  • come ottieni le cose da mostrare,
  • e come gestisci gli eventi.

Questo è ciò che serve containers.

Pertanto, un "ben progettato" componentnel modello è simile al seguente:

class FancyAlerter extends Component {
    sendAlert = () => {
        this.props.sendTheAlert()
    }

    render() {
        <div>
          <h1>Today's Fancy Alert is {this.props.fancyInfo}</h1>
          <Button onClick={sendAlert}/>
        </div>
     }
}

Vedere come questo componente ottiene informazioni visualizza da oggetti di scena (che è venuto dal negozio redux via mapStateToProps) e si ottiene anche la sua funzione di azione dai suoi oggetti di scena: sendTheAlert().

Ecco dove mapDispatchToPropsentra: nel corrispondentecontainer

// FancyButtonContainer.js

function mapDispatchToProps(dispatch) {
    return({
        sendTheAlert: () => {dispatch(ALERT_ACTION)}
    })
}

function mapStateToProps(state) {
    return({fancyInfo: "Fancy this:" + state.currentFunnyString})
}

export const FancyButtonContainer = connect(
    mapStateToProps, mapDispatchToProps)(
    FancyAlerter
)

Mi chiedo se riesci a vedere, ora che è l' container 1 a sapere di redux e spedizione, archiviazione, stato e ... roba.

Il componentmodello in, FancyAlerterche fa il rendering, non ha bisogno di sapere nulla di tutto ciò: ottiene il suo metodo per chiamare onClickdal pulsante, tramite i suoi oggetti di scena.

E ... è mapDispatchToPropsstato l'utile mezzo che il redux fornisce per consentire al contenitore di passare facilmente quella funzione nel componente avvolto sui suoi puntelli.

Tutto questo sembra molto simile al todo esempio nei documenti, e un'altra risposta qui, ma ho provato a lanciarlo alla luce del modello per enfatizzare il perché .

(Nota: non è possibile utilizzare mapStateToPropsper lo stesso scopo mapDispatchToPropsdel motivo di base per cui non si ha accesso dispatchall'interno mapStateToProp. Quindi non è possibile utilizzare mapStateToPropsper assegnare al componente spostato un metodo che utilizza dispatch.

Non so perché abbiano scelto di suddividerlo in due funzioni di mappatura: sarebbe stato più ordinato avere mapToProps(state, dispatch, props) IE una funzione per fare entrambe le cose!


1 Nota che ho deliberatamente chiamato il contenitore in modo esplicito FancyButtonContainer, per evidenziare che si tratta di una "cosa" - l'identità (e quindi l'esistenza!) Del contenitore come "una cosa" a volte si perde nella scorciatoia

export default connect(...) ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

sintassi mostrata nella maggior parte degli esempi


5
Vorrei ancora sapere: che dire della funzionalità richiesta non può essere gestita chiamando direttamente store.dispatch dall'interno di un componente?
pixelpax

8
@ user2130130 Nessuno. Si tratta di un buon design. Se hai accoppiato il tuo componente a store.dispatch, quando decidi di fattorizzare il redux o vuoi usarlo in un posto che non è basato su redxu (o qualcos'altro a cui non riesco a pensare ora) sei bloccato con un molti cambiamenti. La tua domanda si pone in generale "perché ci preoccupiamo delle" buone pratiche di progettazione ": ottieni la stessa funzionalità indipendentemente dal codice". mapDispatchToProps viene fornito in modo da poter scrivere componenti ben progettati e disaccoppiati in modo pulito.
GreenAsJade

4
Quello che davvero non capisco qui è ciò che è: ALERT_ACTIONè quella la funzione di azione o quella typeche viene restituita dalla funzione di azione? : / so baffed
Jamie Hutber

1
@JamieHutber Rigorosamente, ALERT_ACTION non ha nulla a che fare con questa domanda. È un argomento valido da inviare e, come accade, nel mio codice proviene da un "generatore di azioni", che restituisce un oggetto che utilizza la spedizione, come descritto redux.js.org/docs/api/Store.html#dispatch . Il punto principale qui è che il modo di chiamare la spedizione è descritto nel contenitore e passato sui puntelli dei componenti. Il componente ottiene funzioni solo dai suoi oggetti di scena, da nessun'altra parte. Il modo "sbagliato" di farlo in questo modello sarebbe passare l'invio al componente ed effettuare la stessa chiamata nel componente.
GreenAsJade,

1
Se hai più creatori di azioni che devono essere passati al componente figlio come oggetti di scena, un'alternativa è avvolgerli con bindActionCreators all'interno di mapDispatchToProps (vedi redux.js.org/docs/api/bindActionCreators.html ); un'altra alternativa è semplicemente fornire un oggetto di creatori di azioni a connect (), ad esempio connect (mapStateToProps, {actioncreators}), reagire-redux li avvolgerà con dispatch () per te.
dave,

81

È fondamentalmente una scorciatoia. Quindi, invece di dover scrivere:

this.props.dispatch(toggleTodo(id));

Dovresti usare mapDispatchToProps come mostrato nel tuo codice di esempio, e poi altrove scrivi:

this.props.onTodoClick(id);

o più probabilmente in questo caso, lo avresti come gestore dell'evento:

<MyComponent onClick={this.props.onTodoClick} />

C'è un utile video di Dan Abramov su questo qui: https://egghead.io/lessons/javascript-redux-generating-containers-with-connect-from-react-redux-visibletodolist


Grazie. Mi chiedo anche, come viene aggiunta la spedizione agli oggetti di scena in primo luogo?
Code Whisperer,

14
Se non fornisci la tua mapDispatchfunzione, Redux utilizzerà un valore predefinito. Quella mapDispatchfunzione predefinita prende semplicemente il dispatchriferimento della funzione e te lo dà come this.props.dispatch.
Markerikson,

7
Il metodo di spedizione viene tramandato dal Componente
Diego Haz,

55

mapStateToProps()è un'utilità che consente al componente di ottenere lo stato aggiornato (che viene aggiornato da alcuni altri componenti),
mapDispatchToProps()è un'utilità che aiuterà il componente a generare un evento di azione (azione di invio che può causare il cambiamento dello stato dell'applicazione)


23

mapStateToProps, mapDispatchToPropsE connectda react-reduxlibreria fornisce un modo conveniente per accedere alla vostra statee la dispatchfunzione del vostro negozio. Quindi fondamentalmente connect è un componente di ordine superiore, puoi anche pensare a un wrapper se questo ha senso per te. Quindi ogni volta che statesi cambia mapStateToPropsviene chiamato con il nuovo statee successivamente mentre si propsaggiorna il componente verrà eseguita la funzione di rendering per renderizzare il componente nel browser. mapDispatchToPropsmemorizza anche valori-chiave sul propscomponente, in genere assumono la forma di una funzione. In tal modo è possibile attivare il statecambiamento dal componente onClick, dagli onChangeeventi.

Da documenti:

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

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

function toggleTodo(index) {
  return { type: TOGGLE_TODO, index }
}

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

Assicurati anche di avere familiarità con le funzioni stateless di React e i componenti di ordine superiore


Quindi la spedizione è fondamentalmente come un evento?
Codice Whisperer,

2
Potrebbe essere correlato a un evento, l' invio è solo una funzione e l'unico modo per modificare lo stato dell'applicazione . mapStateToProps è un modo per esporre la funzione di spedizione del tuo negozio a Componente React. Si noti inoltre che connect non fa parte di Redux, in realtà è solo un'utilità e una libreria di riduzione della caldaia chiamata React-Redux per funzionare con React e Redux. Puoi ottenere lo stesso risultato senza reagire al flusso se passi il tuo negozio dal componente di reazione principale ai bambini.
Vlad Filimon,

3

mapStateToPropsriceve statee propse ti consente di estrarre oggetti di scena dallo stato da passare al componente.

mapDispatchToPropsriceve dispatched propsè destinato a vincolare i creatori di azioni da inviare, quindi quando si esegue la funzione risultante l'azione viene inviata.

Trovo che questo ti salvi solo dal dover fare dispatch(actionCreator())all'interno del tuo componente, rendendolo così un po 'più facile da leggere.

https://github.com/reactjs/react-redux/blob/master/docs/api.md#arguments


Grazie, ma qual è il valore del dispatchmetodo? Da dove viene?
Codice Whisperer,

Oh. Il dispacciamento fondamentalmente fa sì che l'azione avvii il flusso unidirezionale redux / flux. Le altre risposte sembrano aver risposto alla tua domanda meglio di così.
Harry Moreno,

Inoltre, il metodo di spedizione deriva dal miglioramento fornito dallo <Provider/>stesso. Fa la propria magia di componenti di alto ordine per garantire che la spedizione sia disponibile nei suoi componenti figlio.
Ricardo Magalhães,

3

Supponiamo ora che ci sia un'azione per il redux come:

export function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

Quando lo importi,

import {addTodo} from './actions';

class Greeting extends React.Component {

    handleOnClick = () => {
        this.props.onTodoClick(); // This prop acts as key to callback prop for mapDispatchToProps
    }

    render() {
        return <button onClick={this.handleOnClick}>Hello Redux</button>;
    }
}

const mapDispatchToProps = dispatch => {
    return {
      onTodoClick: () => { // handles onTodoClick prop's call here
        dispatch(addTodo())
      }
    }
}

export default connect(
    null,
    mapDispatchToProps
)(Greeting);

Come dice il nome della funzione mapDispatchToProps(), associa l' dispatchazione ai puntelli (i puntelli del nostro componente)

Quindi prop onTodoClickè una chiave per la mapDispatchToPropsfunzione che delega ulteriormente per l'invio di azioni addTodo.

Inoltre, se desideri tagliare il codice e bypassare l'implementazione manuale, puoi farlo,

import {addTodo} from './actions';
class Greeting extends React.Component {

    handleOnClick = () => {
        this.props.addTodo();
    }

    render() {
        return <button onClick={this.handleOnClick}>Hello Redux</button>;
    }
}

export default connect(
    null,
    {addTodo}
)(Greeting);

Il che significa esattamente

const mapDispatchToProps = dispatch => {
    return {
      addTodo: () => { 
        dispatch(addTodo())
      }
    }
}
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.