2016-03-11 25 views
21

Sto cercando di capire come modificare lo stato di nidificazione profonda in redux. Per me ha senso unire i riduttori e cambiare le proprietà di primo livello dello stato per quei pezzi. Dove non sono così chiaro è su come cambiare lo stato di una proprietà profondamente annidata. Facciamo finta di avere un'app del carrello. Qui di seguito è il mio stato:Riduttori redux: modifica dello stato profondamente annidato

{ 
    cart: { 
     items: [] 
    }, 
    account: { 
     amountLeft: 100, 
     discounts: { 
     redeemed: [], 
     coupons: { 
      buyOneGetOne: false 
     } 
     } 
    } 
} 

Quando un utente inserisce un codice, diciamo che possono riscattare il coupon "buyOneGetOne" e che valore dovrebbe diventare realtà. Ho un riduttore per il carrello e un altro per conto. Per una proprietà di primo livello (come se stavo Cancellazione degli elementi del carrello), vorrei solo fare quanto segue nel mio riduttore:

case 'EMPTY_CART': 
    return Object.assign({}, state, {items: []}); 

Per cambiare buyOneGetOne, tuttavia, sembra che io desidero innanzitutto bisogno di fare una Object.assign sui tagliandi (perché buyOneGetOne è stato modificato), quindi facendo un Object.assign sugli sconti (perché avevo tagliandi modificati), e infine emettendo l'azione in modo che il riduttore potesse fare Object.assign sull'account (perché gli sconti ora hanno cambiato). Questo sembra davvero complicato e facile da fare male, però, il che mi porta a credere che ci debba essere un modo migliore.

Sto andando in giro tutto sbagliato? Sembra che i riduttori siano utilizzati solo per modificare le proprietà del livello radice dello stato (come carrello e account) e che non debba avere un riduttore che tocchi stato all'interno dell'account (come un riduttore di sconti), perché l'account ha già un riduttore . Ma quando voglio solo cambiare una proprietà molto in basso nell'albero dello stato, diventa complesso unire ogni oggetto da quel cambiamento lungo tutta la catena degli oggetti al figlio della radice ...

Puoi/dovresti avere riduttori all'interno di riduttori, come in questo caso avere un riduttore di sconti?

risposta

25

Si può sicuramente avere riduttori all'interno di riduttori; infatti, l'app demo di redux fa questo: http://redux.js.org/docs/basics/Reducers.html.

Per esempio, si potrebbe fare un riduttore nidificato chiamato `sconti:

function discounts(state, action) { 
    switch (action.type) { 
     case ADD_DISCOUNT: 
      return state.concat([action.payload]); 

     // etc etc 
    } 
} 

E poi fare uso di questa riduttore nel tuo account riduttore:

function account(state, action) { 
    switch (action.type) { 
     case ADD_DISCOUNT: 
      return { 
       ...state, 
       discounts: discounts(state.discounts, action) 
      }; 

     // etc etc 
    } 
} 

Per imparare più, controlla lo egghead.io redux series di Dan stesso, in particolare il video reducer composition!

+1

Dannazione, battimi di pochi secondi - ottima risposta però! – mxstbr

+7

Infatti, puoi usare 'combinaReduttori()' più volte nella tua app. Vi consiglio di controllare l'esempio [shopping-cart] (https://github.com/reactjs/redux/tree/master/examples/shopping-cart/reducers) nel repository Redux che dimostra diversi modelli di composizione del riduttore. –

+3

È bello, non me ne sono reso conto. Tuttavia, l'obiettivo non è quello di avere un albero di stato il più possibile piatto? Dov'è l'equilibrio tra i riduttori annidati e un albero di stato piatto? – Skitterm

3

si dovrebbe nido combinedReducers per ogni livello profondo

0

Sì, separando questi riduttori è una cosa giusta. In realtà, se temi che alcune di queste azioni richiedano il cambio di altri riduttori, puoi reagire ad altre costanti o semplicemente inviare un'altra azione.

I've created a library per risolvere questo problema, per consentire una composizione semplice e per evitare la sintassi dettagliata insieme al risultato dell'unione. Così, il vostro esempio sarà simile che:

import { createTile, createSyncTile } from 'redux-tiles'; 

const cartItems = createSyncTile({ 
    type: ['account', 'cart'], 
    fn: ({ params }) => ({ items: params.items }) 
}); 

const coupons = createSyncTile({ 
    type: ['account', 'coupons'], 
    fn: ({ params }) => params, 
}); 

const reedemedCoupons = createSyncTile({ 
    type: ['account', 'reedemedCoupons'], 
    fn: ({ params }) => params.coupons 
}); 

const account = createTile({ 
    type: ['account', 'info'], 
    fn: ({ api }) => api.get('/client/info') 
}); 

Usando questa strategia ogni componente è atomico, e si può facilmente creare nuovi e spedizione altri, senza influenzare gli altri, rende più facile refactoring e rimuovere alcune funzionalità in il futuro, quando le tue "tessere" seguono un unico principio di responsabilità.

Problemi correlati