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

Более чистый/более короткий способ обновления вложенного состояния в Redux?

Иногда редукторы становятся беспорядочными:

const initialState = {
    notificationBar: {
        open: false,
    },
};

export default function (state = initialState, action) {
  switch (action.type) {
    case actions.LAYOUT_NOTIFICATIONBAR_OPEN:
      return Object.assign({}, state, {
        // TODO: Find a cleaner way to do this!
        notificationBar: Object.assign({}, state.notificationBar, {
          open: true,
        }),
      });
    default:
      return state;
  }
}

Есть ли более простой способ сделать это?

4b9b3361

Ответ 2

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

Вернуть литерал

Если вы уверены, что ваше состояние не будет расти, вы можете просто вернуть все новое состояние как литерал.

return {
  notificationBar: {
    open: true
  }
}

Однако это не всегда будет уместным, потому что маловероятно, что ваше состояние будет таким простым.


Объединить редукторы

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

 createStore(combineReducers({
   notificationBar: function(state=initialNBarState, action) {
     switch (action.type) {
       case actions.LAYOUT_NOTIFICATIONBAR_OPEN:
         return Object.assign({}, state, { open: true });
   }
 });

Это не позволяет вам беспокоиться о верхнем уровне свойств, поэтому вы можете избежать наложения вызовов на Object.assign.

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


Использовать неизменяемые данные

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

Мори

Mori - результат компиляции структур данных Clojure и функционального API в JS.

import { hashMap, updateIn } from 'mori';

const initialState = hashMap(
  "notificationBar", hashMap(
    "open", false
  )
);

// ...

return updateIn(state, ['notificationBar', 'open'], true);

ImmutableJS

ImmutableJS является более императивным подходом к приведению семантики Hash Array Mapped Tries из Clojure постоянных структур данных в Javascript.

import { Map } from 'immutable';

const initialState = Map({
  notificationBar: Map({
    open: true
  });
});

// ...

return state.setIn(['notificationBar', 'open'], true);

Псевдоним Object.assign

Вы можете создать более дружелюбную версию Object.assign для написания версий кода выше. Фактически, он может быть почти таким же кратким, как и оператор ....

function $set(...objects) {
  return Object.assign({}, ...objects);
}

return $set(state, {
  notificationBar: $set(state.notificationBar, {
    open: true,
  })
});

Используйте постоянных помощников

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

реагирующие-аддонов-обновление

У React был встроенный набор помощников по неизменности в течение длительного времени. Они используют аналогичный синтаксис для запросов MongoDB.

import update from 'react-addons-update';

return update(state, {
  notificationBar: {
    open: { $set: true }
  }
});

точка-проп-неизменны

В этой библиотеке вы можете использовать знакомые пути точек, чтобы указать обновления (вложенные) свойства.

import dotProp from 'dot-prop-immutable';

return dotProp.set(state, 'notificationBar.open', true);

обновить в

Эта библиотека является оберткой вокруг react-addons-update и предоставляет более функциональный синтаксис для обновления (вложенных) свойств.

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

import updateIn from 'update-in';

return updateIn(state, ['notificationBar', 'open'], () => true);

неизменного-путь

Для обновления свойств эта библиотека похожа на крест между dot-prop-immutable и update-in.

import path from 'immutable-path';

return path.map(state, 'notificationBar.open', () => true);

Ответ 3

Вы можете использовать объективы.

import { set, makeLenses } from '@DrBoolean/lenses'

const L = makeLenses(['notificationBar', 'open']);
const notificationBarOpen = compose(L.notificationBar, L.open)
const setNotificationBarOpenTrue = set(notificationBarOpen, true)

const a = { notificationBar: { open: false } }
const b = setNotificationBarOpenTrue(a) 
// `a` is left unchanged and `b` is `{ notificationBar: { open: true } }`

Вы можете думать о объективах как о доступе/обновлении свойств композиции.

Некоторые хорошие ресурсы о объективах:

Если вы в порядке с чтением lisps, я бы также рекомендовал взглянуть на это превосходное представление для линз из racket docs. Наконец, если вы хотите пойти глубже и нормально читать haskell, вы можете смотреть: Объективы - доступ к композиционным данным и манипуляции.

Ответ 4

В дополнение к тому, что было сказано ранее, вот функциональный способ: Ramda:

import { assocPath } from 'ramda';

const o1 = { a: { b: { c: 1 }, bb: { cc: 22 } } };
const o2 = assocPath(['a', 'b', 'c'])(42)(o1);
console.log(o1 !== o2, o1.a !== o2.a); // new copies of "changed" objects
console.log(o1.a.bb === o2.a.bb); // deep unchanged properties are copied by reference

Ответ 5

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

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

import { createSyncTile } from 'redux-tiles';
const uiTile = createSyncTile({
  type: ['ui', 'elements'],
  fn: ({ params }) => params,
  // type will be `notificationBar`
  nesting: ({ type }) => [type],
});

И что он - он будет корректно обновляться при произвольном вложенности. Кроме того, tile предоставляет селектора, поэтому вам не нужно беспокоиться о том, где именно данные находятся, вы можете просто использовать их. Поэтому я не хочу сказать, что это лучшее решение, но идея довольно проста: не бойтесь писать свою собственную реализацию, а затем просто используйте factory для решения этой проблемы.

Ответ 6

Если вы используете Immutable.js, вы можете найти в разделе Вложенные структуры некоторые функции, которые могут помогите вам, я лично использую mergeDeep:

prevState.mergeDeep({ userInfo: {
  username: action.payload.username,
} }),