Redux: повторно использовать редуктор для обновления нескольких свойств состояния - PullRequest
0 голосов
/ 21 сентября 2018

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

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

Скажем, состояние выглядит следующим образом:

{
  items: {
    house: { isBig:true, location: ... },
    car: { isMine:true, isBroken:false, ... }
  },
  books: [
    { id:1, available:true, title:... },
    { id:2, available:false, title:... }, ...
  ]
}

Каков правильный механизм создания различных экземпляров MySwitch для просмотра и редактирования любого заданного значения - например, items.house.isBig, items.car.isMine или books[1].available - и обновления их с помощью одного и того же редуктора?

Я мог бы внедрить свойство в компонент, используя connect, что-то вроде:

ASwitch = connect(state => ({
  valueToEdit: state.items.car.isMine
})(MySwitch)

, но я понятия не имею, как передать его в редуктор и как позволить ему обновить соответствующую частьгосударство.Я думал, что смогу указать путь (то есть "items.car.isMine") к действию:

export const editBoolean = ( path, value ) => {
  return {
    type: 'BOOL_EDIT',
    payload: { path, value }
  }
}

, а затем использовать что-то подобное в редукторе:

case 'BOOL_EDIT': {
  const { path, value } = action.payload;
  return {
    ...state,
    [path]: value
  }
}

, но это не такПохоже, что ES6 не поддерживает переменные вычисляемые свойства, вместо этого создается свойство типа state["items.car.isMine"].

Я не знаю, как это сделать.Спасибо за любую помощь.

Ответы [ 2 ]

0 голосов
/ 21 сентября 2018

* Отказ от ответственности: для меня это своего рода анти-паттерн Redux, так как он позволяет установить любое значение в хранилище на любое значение, используя одно действие.Используйте это с умом.

````
const set = require("lodash/set");
const { produce } = require("immer");

case 'BOOL_EDIT': {
  const { path, value } = action.payload;
  return produce(state, draft => {
    set(draft, path, value);
  });
}
```

editBoolean("items.car.isMine", false)
0 голосов
/ 21 сентября 2018

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

Вы можете использовать что-то вроде этого

const singlePathReducer = (state, { payload: { path, value } }) => path.reduce(({ nextState, branch = nextState }), node) => {
  if (node !== path[path.length - 1]) {
    // this was the part you were missing; you need to walk down your state tree
    // by passing a reference of the current branch to the next reduce callback
    return { nextState, branch: nextState[node] };
  }

  // mutate your nextState, this is ok
  branch[node] = value;
  return nextState;

  // don't mutate state, create a copy
}, { nextState: { ...state } });

Если я не ошибаюсь относительно того, как передача по ссылке работает в js, это должно работать.Полная реализация будет выглядеть следующим образом (разделение singlePathReducer fn позволяет упростить модульное тестирование):

const rootReducer = (state, action) => {
  switch action.type {
    case 'BOOL_EDIT':
      return singlePathReducer(state, action);
    default:
      return state;
  }
};
...