Я использую Redux. В моем редукторе я пытаюсь удалить свойство из такого объекта:
const state = {
a: '1',
b: '2',
c: {
x: '42',
y: '43'
},
}
И я хочу иметь что-то подобное, не изменяя исходное состояние:
const newState = {
a: '1',
b: '2',
c: {
x: '42',
},
}
Я старался:
let newState = Object.assign({}, state);
delete newState.c.y
но по некоторым причинам он удаляет свойство из обоих состояний.
Могли бы мне помочь в этом?
javascript
immutability
redux
Винсент Тэйнг
источник
источник
Object.assign
создает лишь неполную копию изstate
и , следовательно ,state.c
иnewState.c
будет указывать на тот же объект разделяемого. Вы пытались удалить свойствоy
из общего объекта,c
а не из нового объектаnewState
.Ответы:
Как насчет использования синтаксиса присваивания деструктуризации ?
const original = { foo: 'bar', stack: 'overflow', }; // If the name of the property to remove is constant const { stack, ...withoutFirst } = original; console.log(withoutFirst); // Will be { "foo": "bar" } // If the name of the property to remove is from a variable const key = 'stack' const { [key]: value, ...withoutSecond } = original; console.log(withoutSecond); // Will be { "foo": "bar" } // To do a deep removal with property names from variables const deep = { foo: 'bar', c: { x: 1, y: 2 } }; const parentKey = 'c'; const childKey = 'y'; // Remove the 'c' element from original const { [parentKey]: parentValue, ...noChild } = deep; // Remove the 'y' from the 'c' element const { [childKey]: removedValue, ...childWithout } = parentValue; // Merge back together const withoutThird = { ...noChild, [parentKey]: childWithout }; console.log(withoutThird); // Will be { "foo": "bar", "c": { "x": 1 } }
источник
const deleteProperty = ({[key]: _, ...newObj}, key) => newObj;
. Использование:deleteProperty({a:1, b:2}, "a");
дает{b:2}
deep['c']
он пуст, поэтому в общем случае вы можете добавить проверку наличия ключа.Мне нравятся методы массива ES5
filter
,map
иreduce
они полезны, потому что они всегда возвращают новые массивы или объекты. В этом случае я быObject.keys
перебрал объект иArray#reduce
снова превратил его в объект.return Object.assign({}, state, { c: Object.keys(state.c).reduce((result, key) => { if (key !== 'y') { result[key] = state.c[key]; } return result; }, {}) });
источник
myObject
сmyKey
удаленным ключом :Object.keys(myObject).reduce((acc, cur) => cur === myKey ? acc : {...acc, [cur]: myObject[cur]}, {})
Вы можете использовать
_.omit(object, [paths])
из библиотеки lodashпуть может быть вложенным, например:
_.omit(object, ['key1.key2.key3'])
источник
_.omit
невозможно удалить глубокие свойства (о чем спрашивал OP). Для этого естьomit-deep-lodash
модуль._.cloneDeep(obj)
из lodash. Это легко копирует объект, а затем вы можете просто использовать jsdelete obj.[key]
для удаления ключа.Просто используйте функцию деструктуризации объекта ES6
const state = { c: { x: '42', y: '43' }, } const { c: { y, ...c } } = state // generates a new 'c' without 'y' console.log({...state, c }) // put the new c on a new state
источник
const {y, ...c} = state.c
может быть немного яснее, чем дваc
с левой стороны.const name = 'c'
тогда вы можете сделать это, аconst {[name]:deletedValue, ...newState} = state
затем вернутьсяnewState
в свой редуктор. Это для удаления ключа верхнего уровняЭто потому, что вы копируете значение
state.c
в другой объект. И это значение является указателем на другой объект javascript. Итак, оба этих указателя указывают на один и тот же объект.Попробуй это:
let newState = Object.assign({}, state); console.log(newState == state); // false console.log(newState.c == state.c); // true newState.c = Object.assign({}, state.c); console.log(newState.c == state.c); // now it is false delete newState.c.y;
Вы также можете сделать глубокую копию объекта. Просмотрите этот вопрос, и вы найдете то, что лучше всего для вас.
источник
state.c
является ссылкой, и ссылка копируется нормально. Redux хочет нормализованную форму состояния, что означает использование идентификаторов вместо ссылок при вложении состояния. Ознакомьтесь с документами reduxКак насчет этого:
function removeByKey (myObj, deleteKey) { return Object.keys(myObj) .filter(key => key !== deleteKey) .reduce((result, current) => { result[current] = myObj[current]; return result; }, {}); }
Он фильтрует ключ, который следует удалить, а затем создает новый объект из оставшихся ключей и исходного объекта. Идея позаимствована у Тайлера МакГиннеса из классной программы reactjs.
JSBin
источник
function dissoc(key, obj) { let copy = Object.assign({}, obj) delete copy[key] return copy }
Кроме того, если вам нужен инструментарий функционального программирования, обратите внимание на Ramda .
источник
Вы можете использовать помощник неизменяемости , чтобы отключить атрибут, в вашем случае:
import update from 'immutability-helper'; const updatedState = update(state, { c: { $unset: ['y'] } });
источник
С 2019 года есть еще один вариант - использовать этот
Object.fromEntries
метод. Он достиг стадии 4.const newC = Object.fromEntries( Object.entries(state.c).filter(([key]) => key != 'y') ) const newState = {...state, c: newC}
Приятно то, что он прекрасно обрабатывает целочисленные ключи.
источник
С Immutable.js это просто :
const newState = state.deleteIn(['c', 'y']);
описание deleteIn ()
источник
Проблема в том, что вы не глубоко клонируете свое исходное состояние. Итак, у вас есть неглубокая копия.
Вы можете использовать оператор распространения
const newState = { ...state, c: { ...state.c } }; delete newState.c.y
Или следуя тому же коду
let newState = Object.assign({}, state, { c: Object.assign({}, state.c) }); delete newState.c.y
источник
Я обычно использую
Object.assign({}, existingState, {propToRemove: undefined})
Я понимаю, что на самом деле это не удаляет свойство, а почти для всех целей 1 его функциональный эквивалент. Синтаксис для этого намного проще, чем альтернативы, что, как мне кажется, является довольно хорошим компромиссом.
1 Если вы используете
hasOwnProperty()
, вам нужно будет использовать более сложное решение.источник
Я использую этот шаблон
const newState = Object.assign({}, state); delete newState.show; return newState;
но в книге я увидел другой образец
return Object.assign({}, state, { name: undefined } )
источник
полезность ;))
const removeObjectField = (obj, field) => { // delete filter[selectName]; -> this mutates. const { [field]: remove, ...rest } = obj; return rest; }
тип действия
const MY_Y_REMOVE = 'MY_Y_REMOVE';
создатель действий
const myYRemoveAction = (c, y) => { const result = removeObjectField(c, y); return dispatch => dispatch({ type: MY_Y_REMOVE, payload: result }) }
редуктор
export default (state ={}, action) => { switch (action.type) { case myActions.MY_Y_REMOVE || : return { ...state, c: action.payload }; default: return state; } };
источник
Как уже упоминалось в некоторых ответах, это потому, что вы пытаетесь изменить вложенное состояние, т.е. на один уровень глубже. Каноническим решением было бы добавить редуктор на
x
уровне состояния:const state = { a: '1', b: '2', c: { x: '42', y: '43' }, }
Редуктор более глубокого уровня
let newDeepState = Object.assign({}, state.c); delete newDeepState.y;
Оригинальный редуктор уровня
let newState = Object.assign({}, state, {c: newDeepState});
источник