Подтвердить что ты не робот

Редукторы 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 на счету (поскольку скидка теперь изменилась). Это кажется действительно сложным и легким для того, чтобы сделать что-то неправильное, но это заставляет меня думать, что должен быть лучший способ.

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

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

4b9b3361

Ответ 1

У вас могут быть редукторы внутри редукторов; на самом деле, demo-приложение 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 самим Дэном, в частности редукционная композиция видео!

Ответ 2

вам нужно вложить combinedReducers для каждого уровня глубиной

Ответ 3

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

Я создал библиотеку для решения этой проблемы - чтобы упростить составление и избежать подробного синтаксиса, а также слияние результата, Итак, ваш пример будет выглядеть следующим образом:

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

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