оператор распространения против immutable.js - PullRequest
0 голосов
/ 23 ноября 2018

Похоже, что при использовании redux с реагировать, immutable.js стал почти отраслевым стандартом.Мой вопрос заключается в том, не вносим ли мы неизменные изменения в наши состояния редукса, когда используем оператор распространения?Например,

const reducer = (state=initialState, action) => {
    switch(action.type){
        case actionType.SOME_ACTION:
            return {
                ...state,
                someState: state.someState.filter(etc=>etc)
            }
    }

Разве я не устанавливаю состояние с помощью приставки неизменным?Какая польза от использования immutable.js OVER распространяющего оператора способа сделать объекты неизменяемыми?

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

Ответы [ 2 ]

0 голосов
/ 17 декабря 2018

Разве я не устанавливаю состояние с помощью Redux неизменным?

В вашем примере кода (при условии, что реальная функция, переданная в filter, не выполняет мутации), да.

В чем преимущество использования immutable.js OVER-оператора для создания объектов неизменяемым?

Две основные причины:

  1. Невозможно (легко) случайно изменить объект коллекции Immutable, так как общедоступный API этого не разрешает.В то время как со встроенными коллекциями JS это так.Глубокая заморозка (рекурсивный вызов Object.freeze) может немного помочь в этом.

  2. Эффективное * использование неизменяемых обновлений со встроенными коллекциями может быть сложной задачей.Immutable.js использует попытки внутри, чтобы сделать обновления более эффективными, чем это возможно при использовании vanilla с использованием собственных коллекций.

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

* Эффективное значениевременная сложность, например, строительства объекта и работы ГХ из-за увеличения оттока объекта.

0 голосов
/ 23 ноября 2018

Краткий ответ

Да!Оператор распространения ES6 может использоваться как замена для immutable.js целиком, но есть одно важное предостережение: вы всегда должны поддерживать ситуационную осведомленность.

ОЧЕНЬ Длинный ответ

Вы иваши коллеги-разработчики будут нести ответственность за поддержание неизменности на 100%, а не позволять им делать это за вас.Вот подробное описание того, как вы можете самостоятельно управлять неизменяемым состоянием, используя ES6 «оператор распространения», и его различные функции, такие как filter и map.

. Далее будет рассмотрено удаление и добавление значений вмассив или объект неизменным и мутированным способом.Я выхожу из системы initialState и newState в каждом примере, чтобы продемонстрировать, что мы мутировали initialState.Это важно по той причине, что Redux не будет инструктировать пользовательский интерфейс для повторного рендеринга, если initialState и newState точно такие же.

Примечание: Immutable.js будетсбой приложения, если вы попробовали любое из приведенных ниже мутантных решений.

Удалите элемент из массива

Неизменяемым образом

const initialState = {
  members: ['Pete', 'Paul', 'George', 'John']
}
const reducer = (state, action) => {
  switch(action.type){
case 'REMOVE_MEMBER':
  return {
    ...state,
    members: state.members.filter(
      member => member !== action.member
    )
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'REMOVE_MEMBER', member: 'Pete'}
);

console.log('initialState', initialState);
console.log('newState', newState);

Мутированный путь

const initialState = {
  members: ['Pete', 'Paul', 'George', 'John']
}
const reducer = (state, action) => {
  switch(action.type){
case 'REMOVE_MEMBER':
  state.members.forEach((member, i) => {
    if (member === action.member) {
      state.members.splice(i, 1)
    }
  })
  return {
    ...state,
    members: state.members
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'REMOVE_MEMBER', member: 'Pete'}
);

console.log('initialState', initialState);
console.log('newState', newState);

Добавить элемент в массив

Неизменяемый способ

const initialState = {
  members: ['Paul', 'George', 'John']
}
const reducer = (state, action) => {
  switch(action.type){
case 'ADD_MEMBER':
  return {
    ...state,
    members: [...state.members, action.member]
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'ADD_MEMBER', member: 'Ringo'}
);

console.log('initialState', initialState);
console.log('newState', newState);

Мутированный путь

const initialState = {
  members: ['Paul', 'George', 'John']
}
const reducer = (state, action) => {
  switch(action.type){
case 'ADD_MEMBER':
  state.members.push(action.member);
  return {
    ...state,
    members: state.members
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'ADD_MEMBER', member: 'Ringo'}
);

console.log('initialState', initialState);
console.log('newState', newState);

Обновление массива

Неизменяемый способ

const initialState = {
  members: ['Paul', 'Pete', 'George', 'John']
}
const reducer = (state, action) => {
  switch(action.type){
case 'UPDATE_MEMBER':
  return {
    ...state,
    members: state.members.map(member => member === action.member ? action.replacement : member)
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'UPDATE_MEMBER', member: 'Pete', replacement: 'Ringo'}
);

console.log('initialState', initialState);
console.log('newState', newState);

Мутированный путь

const initialState = {
  members: ['Paul', 'Pete', 'George', 'John']
}
const reducer = (state, action) => {
  switch(action.type){
case 'UPDATE_MEMBER':
  state.members.forEach((member, i) => {
    if (member === action.member) {
      state.members[i] = action.replacement;
    }
  })
  return {
    ...state,
    members: state.members
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'UPDATE_MEMBER', member: 'Pete', replacement: 'Ringo'}
);

console.log('initialState', initialState);
console.log('newState', newState);

Объединение массивов

Неизменяемый способ

const initialState = {
  members: ['Paul', 'Ringo']
}
const reducer = (state, action) => {
  switch(action.type){
case 'MERGE_MEMBERS':
  return {
    ...state,
    members: [...state.members, ...action.members]
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'MERGE_MEMBERS', members: ['George', 'John']}
);

console.log('initialState', initialState);
console.log('newState', newState);

Мутировал

const initialState = {
  members: ['Paul', 'Ringo']
}
const reducer = (state, action) => {
  switch(action.type){
case 'MERGE_MEMBERS':
  action.members.forEach(member => state.members.push(member))
  return {
    ...state,
    members: state.members
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'MERGE_MEMBERS', members: ['George', 'John']}
);

console.log('initialState', initialState);
console.log('newState', newState);

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

Удалить из объекта

Неизменяемый способ

const initialState = {
  members: {
paul: {
  name: 'Paul',
  instrument: 'Guitar'
},
stuart: {
  name: 'Stuart',
  instrument: 'Bass'
}
  }
}
const reducer = (state, action) => {
  switch(action.type){
case 'REMOVE_MEMBER':
  let { [action.member]: _, ...members } = state.members
  return {
    ...state,
    members
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'REMOVE_MEMBER', member: 'stuart'}
);

console.log('initialState', initialState);
console.log('newState', newState);

Мутировал

const initialState = {
  members: {
paul: {
  name: 'Paul',
  instrument: 'Guitar'
},
stuart: {
  name: 'Stuart',
  instrument: 'Bass'
}
  }
}
const reducer = (state, action) => {
  switch(action.type){
case 'REMOVE_MEMBER':
  delete state.members[action.member]
  return {
    ...state,
    members: state.members
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'REMOVE_MEMBER', member: 'stuart'}
);

console.log('initialState', initialState);
console.log('newState', newState);

Обновление объекта

Неизменяемый способ

const initialState = {
  members: {
paul: {
  name: 'Paul',
  instrument: 'Guitar'
},
ringo: {
  name: 'George',
  instrument: 'Guitar'
}
  }
}
const reducer = (state, action) => {
  switch(action.type){
case 'CHANGE_INSTRUMENT':
  return {
    ...state,
    members: {
      ...state.members,
      [action.key]: {
        ...state.members[action.member],
        instrument: action.instrument
      }
    }
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'CHANGE_INSTRUMENT', member: 'paul', instrument: 'Bass'}
);

console.log('initialState', initialState);
console.log('newState', newState);

Мутировал

const initialState = {
  members: {
paul: {
  name: 'Paul',
  instrument: 'Guitar'
},
ringo: {
  name: 'George',
  instrument: 'Guitar'
}
  }
}
const reducer = (state, action) => {
  switch(action.type){
case 'CHANGE_INSTRUMENT':
  state.members[action.member].instrument = action.instrument
  return {
    ...state,
    members: state.members
  }
  }
}
const newState = reducer(
  initialState,
  {type: 'CHANGE_INSTRUMENT', member: 'paul', instrument: 'Bass'}
);

console.log('initialState', initialState);
console.log('newState', newState);

Если вы сделали это так далеко, поздравляем!Я знаю, что этот пост был длинным, но я чувствовал, что важно продемонстрировать все мутированные способы , которые вам понадобятся, чтобы предотвратить себя без Immutable.js.Одним из огромных преимуществ использования Immutable.js, помимо того, что мешает писать плохой код, являются вспомогательные методы, такие как mergeDeep и updateIn

Immutable.JS

mergeDeep

const initialState = Immutable.fromJS({
  members: {
    paul: {
      name: 'Paul',
      instrument: 'Guitar'
    },
    ringo: {
      name: 'George',
      instrument: 'Guitar'
    }
  }
})
const reducer = (state, action) => {
  switch (action.type) {
    case 'ADD_MEMBERS':
      return state.mergeDeep({members: action.members})
  }
}
const newState = reducer(
  initialState,
  {
    type: 'ADD_MEMBERS',
    members: {
      george: { name: 'George', instrument: 'Guitar' },
      john: { name: 'John', instrument: 'Guitar' }
    }
  }
);

console.log('initialState', initialState);
console.log('newState', newState);
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.min.js"></script>

updateIn

const initialState = Immutable.fromJS({
  members: {
    paul: {
      name: 'Paul',
      instrument: 'Guitar'
    },
    ringo: {
      name: 'George',
      instrument: 'Guitar'
    }
  }
})
const reducer = (state, action) => {
  switch (action.type) {
    case 'CHANGE_INSTRUMENT':
      return state.updateIn(['members', action.member, 'instrument'], instrument => action.instrument)
  }
}
const newState = reducer(
  initialState,
  {type: 'CHANGE_INSTRUMENT', member: 'paul', instrument: 'Bass'}
);

console.log('initialState', initialState);
console.log('newState', newState);
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.min.js"></script>
...