2016-03-11 2 views
21

Я пытаюсь обмотать голову, как изменить глубоко вложенное состояние в redux. Мне разумно комбинировать редукторы и менять свойства первого уровня состояния для этих предметов. Там, где я не совсем ясно, как изменить состояние глубоко вложенного имущества. Давайте притворимся, что у меня есть приложение для корзины покупок. Ниже мое состояние:Редукторы Redux - изменение глубоко вложенного состояния

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

Когда пользователь вводит в коде, скажем, они могут выкупить «buyOneGetOne» купон, и это значение должно стать правдой. У меня есть редуктор для тележки, а другой для счета. Для свойства первого уровня (например, если я убирала предметы телеге в), я бы просто сделать следующее в моем редукторе:

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

Для изменения buyOneGetOne, однако, кажется, что я бы сначала нужно сделать Object.assign на купонах (потому что buyOneGetOne был изменен), а затем сделал Object.assign на скидках (потому что я изменил купоны), а затем, наконец, испустил действие, чтобы редуктор мог сделать Object.assign на учетной записи (поскольку сейчас скидки изменено). Это кажется действительно сложным и легким для того, чтобы сделать что-то неправильное, и это заставляет меня думать, что должен быть лучший способ.

Я все об этом не так? Кажется, что редукторы используются только для изменения свойств корневого уровня состояния (например, для корзины и учетной записи) и что у меня не должно быть редуктора, который затрагивает состояние внутри учетной записи (например, редуктор скидок), потому что учетная запись уже имеет редуктор , Но когда я хочу изменить одно свойство далеко по дереву состояний, становится сложно объединить каждый объект из этого изменения вплоть до цепочки объектов к дочернему элементу корня ...

Можете ли вы/должны ли вы есть редукторы внутри редукторов, как в этом случае есть скидки?

ответ

25

У вас могут быть редукторы внутри редукторов; на самом деле, демонстрационное приложение redux делает это: http://redux.js.org/docs/basics/Reducers.html.

Например, вы могли бы сделать вложенный редуктор под названием `скидка:

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

     // etc etc 
    } 
} 

И затем использовать этот редуктор в вашем счете редукторе:

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

     // etc etc 
    } 
} 

Чтобы узнать больше, посмотрите egghead.io redux series самим Дэном, в частности видео reducer composition!

+1

Черт, побей меня на несколько секунд - отличный ответ! – mxstbr

+7

На самом деле вы можете использовать 'combReducers()' более одного раза в своем приложении. Я рекомендую вам просмотреть пример [shopping-cart] (https://github.com/reactjs/redux/tree/master/examples/shopping-cart/reducers) в репозитории Redux, который демонстрирует различные модели редукторной композиции. –

+3

Это круто, я этого не понимал. Однако не цель также иметь как ровное дерево состояния, насколько это возможно? Где баланс между вложенными редукторами и деревом с плоским деревом? – Skitterm

3

вы должны гнездо combinedReducers для каждого уровня глубокого

0

Да, отделяя эти редукторы является правильным. На самом деле, если вы боитесь, что некоторые из этих действий потребуют изменения другого редуктора, вы можете либо отреагировать на другую константу, либо просто отправить другое действие.

I've created a library, чтобы решить эту проблему - чтобы обеспечить легкий состав и избежать подробного синтаксиса, а также слияние результата. Так, ваш пример будет выглядеть так:

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

Используя эту стратегию каждый компонент является атомарным, и вы можете легко создавать новые и отправки другим, не затрагивая другие, что делает его легче реорганизовать и удалить некоторые функциональные возможности в будущее, когда ваши «плитки» следуют принципу единой ответственности.

Смежные вопросы