2016-02-27 11 views
7

Sto creando app usando Redux e React. Mi imbatto in un problema in cui non posso mappare lo stato sulle proprietà del componente poiché lo stato ha una proprietà che corrisponde al nome del riduttore che ho usato.Lo stato in applicazione redux/react ha una proprietà con il nome del riduttore

L'riduce viene creato con combineReducers metodo

const rootReducer = combineReducers({ 
    appReducer 
}) 

Lo stato iniziale è

const initialState = { 
    sources: [], 
    left: {}, 
    right: {}, 
    diff: {} 
} 

Tuttavia nella funzione componente mapStateToProps:

function mapStateToProps(state) { 
    return { 
    sources: state.sources 
    } 
} 

Il state.sources è undefined perché il valoredi state parametro è

{ 
    appReducer: { 
    sources: [], 
    left: {}, 
    right: {}, 
    diff: {} 
    } 
} 

Si tratta di una caratteristica di redux? Quindi, quando uso più riduttori, tutti aggiungeranno nuove proprietà alla variabile state? O c'è qualcosa di sbagliato dalla mia parte (non ho mai notato questo comportamento nelle esercitazioni di Redux).

Grazie

+1

il codice è corretto 'state.appReducer. fonti 'è necessario il nome del riduttore –

+3

Immaginate di avere 2,3 riduttori ogni riduttore ha la proprietà' sources' –

+1

potete ottenere 'sources' specifici da' state.appReducer. sources' e 'state.appReducer.2 sources' –

risposta

17

Se si dispone di un solo riduttore, non hai bisogno di combineReducers(). Basta usare direttamente:

const initialState = { 
    sources: [], 
    left: {}, 
    right: {} 
} 
function app(state = initialState, action) { 
    switch (action.type) { 
    case 'ADD_SOURCE': 
    return Object.assign({}, state, { 
     sources: [...state.sources, action.newSource] 
    }) 
    case 'ADD_SOURCE_TO_LEFT': 
    return Object.assign({}, state, { 
     left: Object.assign({}, state.left, { 
     [action.sourceId]: true 
     }) 
    }) 
    case 'ADD_SOURCE_TO_RIGHT': 
    return Object.assign({}, state, { 
     right: Object.assign({}, state.right, { 
     [action.sourceId]: true 
     }) 
    }) 
    default: 
    return state 
    } 
} 

ora è possibile creare un archivio con quella riduttore:

import { createStore } from 'redux' 
const store = createStore(app) 

e collegare un componente ad esso:

const mapStateToProps = (state) => ({ 
    sources: state.sources 
}) 

Tuttavia, il vostro riduttore è difficile da leggere perché aggiorna molte cose diverse contemporaneamente. Ora, questo è il momento in cui si desidera dividere in diversi riduttori indipendenti:

function sources(state = [], action) { 
    switch (action.type) { 
    case 'ADD_SOURCE': 
    return [...state.sources, action.newSource] 
    default: 
    return state 
    } 
} 

function left(state = {}, action) { 
    switch (action.type) { 
    case 'ADD_SOURCE_TO_LEFT': 
    return Object.assign({}, state, { 
     [action.sourceId]: true 
    }) 
    default: 
    return state 
    }  
} 

function right(state = {}, action) { 
    switch (action.type) { 
    case 'ADD_SOURCE_TO_RIGHT': 
    return Object.assign({}, state, { 
     [action.sourceId]: true 
    }) 
    default: 
    return state 
    }  
} 

function app(state = {}, action) { 
    return { 
    sources: sources(state.sources, action), 
    left: left(state.left, action), 
    right: right(state.right, action), 
    } 
} 

Questo è più facile da mantenere e capire, e rende anche più facile cambiare e riduttori di test in modo indipendente.

Infine, come ultimo passaggio, possiamo usare combineReducers() per generare il riduttore radice app invece di scrivere a mano:

// function app(state = {}, action) { 
// return { 
//  sources: sources(state.sources, action), 
//  left: left(state.left, action), 
//  right: right(state.right, action), 
// } 
// } 

import { combineReducers } from 'redux' 
const app = combineReducers({ 
    sources, 
    left, 
    right 
}) 

Non c'è grande vantaggio di utilizzare combineReducers() invece di scrivere il riduttore di root mano tranne che è leggermente più efficiente e probabilmente ti farà risparmiare alcuni errori di battitura. Inoltre, puoi applicare questo modello più di una volta nella tua app: è bene combinare riduttori non collegati in un unico riduttore più volte in modo annidato.

Tutto questo refactoring non avrebbe alcun effetto sui componenti.

vorrei suggerire di guardare la mia free Egghead course on Redux che copre questo modello di composizione riduttore e mostra come combineReducers() implementata.

+0

Grazie Dan per la spiegazione e il link a quei video. –

+0

Mi piace sempre la tua spiegazione, semplice e accurata. –

5

In realtà, credo che il vostro stato iniziale potrebbe essere:

{ 
    appReducer: { 
    sources: [], 
    left: {}, 
    right: {}, 
    diff: {} 
    } 
} 

Questo perché combineReducers opere prendendo il nome del riduttore, e la mappatura del suo contenuto a quel nome.

Inoltre, solo una nota, ma se si intende utilizzare più di un riduttore, i nomi dei riduttori dovrebbero essere più specifici di appReducer e (solo la mia opinione personale) non hanno bisogno della parola . Un'applicazione tipica potrebbe assomigliare a questo:

combineReducers({ 
    user: userReducer, 
    messages: messagesReducer, 
    notifications: notificationsReducer 
}); 

Quindi, il vostro stato potrebbe essere letta come:

state.user.email 
state.messages[0] 
+0

Hai ragione. Dovrei quindi modificare 'initialState' oggetto di conseguenza? –

+1

L'idea è che ogni ramo del riduttore è responsabile del proprio 'initialState'. Il tuo riduttore di root non dovrebbe avere initialState, e ogni riduttore dovrebbe includere solo la sua roba Quindi il tuo' appReducer' sarebbe un oggetto con le chiavi per fonti, sinistra, destra, ecc. –