Это правильный способ удалить элемент с помощью redux?


118

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

ADD_ITEM: (state, action) => ({
  ...state,
  items: [...state.items, action.payload.value],
  lastUpdated: action.payload.date
})

для добавления элемента - я использую распространение, чтобы добавить элемент в массив.

для удаления я использовал:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: [...state.items.splice(0, action.payload), ...state.items.splice(1)],
  lastUpdated: Date.now() 
})

но это изменяет объект состояния ввода - это запрещено, даже если я возвращаю новый объект?


1
Быстрый вопрос. Splice возвращает элементы, которые вы удалили. Это ваше намерение? В противном случае вам следует использовать slice, который также подчиняется закону об отсутствии мутаций.
— m0meni 03

В этом примере я объединяю две части массива в новый массив - с элементом, который я хотел удалить, оставленным. Slice также возвращает удаленный элемент, верно? Только он делает это без изменения исходного массива, так что это будет лучший подход?
— CWright 03

@ AR7 в соответствии с вашим предложением: items: [...state.items.slice(0, action.payload.value), ...state.items.slice(action.payload.value + 1 )]теперь использовать срез вместо сращивания, чтобы не изменять ввод - это путь или есть более краткий способ?
— CWright 03

Ответы:


209

Нет. Никогда не изменяйте свое состояние.

Даже если вы возвращаете новый объект, вы все равно загрязняете старый объект, чего никогда не стоит делать. Это затрудняет сравнение старого и нового состояния. Например, в shouldComponentUpdateкотором react-redux используется под капотом. Это также делает невозможным путешествие во времени (то есть отмену и повтор).

Вместо этого используйте неизменяемые методы. Всегда используйте Array#sliceи никогдаArray#splice .

Я предполагаю из вашего кода, что action.payloadэто индекс удаляемого элемента. Лучше было бы следующее:

items: [
    ...state.items.slice(0, action.payload),
    ...state.items.slice(action.payload + 1)
],

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

4
Пожалуйста, докажите это на примере jsfiddle / codepen. Фрагмент arr.slice(arr.length)всегда должен создавать пустой массив, независимо от его содержимого arr.
— Дэвид Л. Уолш

1
@ david-l-walsh извините за путаницу, должно быть, я сделал опечатку или что-то в этом роде при тестировании этого примера. Это творит чудеса. Мой единственный вопрос: зачем нужен оператор спреда ...во второй части...state.items.slice(action.payload + 1)
— Таенор

5
Array#sliceвозвращает массив. Чтобы объединить два фрагмента в один массив, я использовал оператор распространения. Без него у вас был бы массив массивов.
— Дэвид Л. Уолш

4
в этом есть смысл. Большое спасибо за разъяснения (и извините за путаницу вначале).
— Thaenor

151

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

return state.filter(element => element !== action.payload);

В контексте вашего кода это будет выглядеть примерно так:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => item !== action.payload),
  lastUpdated: Date.now() 
})

1
Создает ли фильтр новый массив?
— chenop

6
@chenop Да, метод Array.filter возвращает новый массив. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
— Steph M

4
Обратите внимание, что если есть дубликаты, это удалит их ВСЕ. Чтобы использовать фильтр для удаления определенного индекса, вы можете использовать, например,arr.filter((val, i) => i !== action.payload )
— erich2k8

22

Метод ES6 Array.prototype.filterвозвращает новый массив с элементами, соответствующими критериям. Следовательно, в контексте исходного вопроса это будет:

DELETE_ITEM: (state, action) => ({
  ...state,
  items: state.items.filter(item => action.payload !== item),
  lastUpdated: Date.now() 
})

.filter()не является методом ES2015, но был добавлен в предыдущей версии ES5.
— morkro

7

Еще один вариант неизменного редуктора DELETED для массива с объектами:

const index = state.map(item => item.name).indexOf(action.name);
const stateTemp = [
  ...state.slice(0, index),
  ...state.slice(index + 1)
];
return stateTemp;

0

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

В этом сценарии мы пытаемся удалить элемент из свойства состояния.

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

Вот пример того, как удалить вложенный объект:

// ducks/outfits (Parent)

// types
export const NAME = `@outfitsData`;
export const REMOVE_FILTER = `${NAME}/REMOVE_FILTER`;

// initialization
const initialState = {
  isInitiallyLoaded: false,
  outfits: ['Outfit.1', 'Outfit.2'],
  filters: {
    brand: [],
    colour: [],
  },
  error: '',
};

// action creators
export function removeFilter({ field, index }) {
  return {
    type: REMOVE_FILTER,
    field,
    index,
  };
}

export default function reducer(state = initialState, action = {}) {
  sswitch (action.type) {  
  case REMOVE_FILTER:
  return {
    ...state,
    filters: {
    ...state.filters,
       [action.field]: [...state.filters[action.field]]
       .filter((x, index) => index !== action.index)
    },
  };
  default:
     return state;
  }
}

Чтобы лучше понять это, обязательно ознакомьтесь с этой статьей: https://medium.com/better-programming/deleting-an-item-in-a-nested-redux-state-3de0cb3943da

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.