Удалить свойство в объекте неизменно
Я использую 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
но по некоторым причинам он удаляет свойство из обоих состояний.
Помогло бы мне это сделать?
Ответы
Ответ 1
Как насчет использования синтаксиса деструктурирования?
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 } }
Ответ 2
Я нахожу методы массива 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;
}, {})
});
Ответ 3
Вы можете использовать _.omit(object, [paths])
из библиотеки lodash
путь может быть вложенным, например: _.omit(object, ['key1.key2.key3'])
Ответ 4
Это потому, что вы копируете значение 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;
Вы также можете сделать глубокую копию объекта. См. этот вопрос, и вы найдете то, что лучше для вас.
Ответ 5
Просто используйте функцию разрушения объектов 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
Ответ 6
Как насчет этого:
function removeByKey (myObj, deleteKey) {
return Object.keys(myObj)
.filter(key => key !== deleteKey)
.reduce((result, current) => {
result[current] = myObj[current];
return result;
}, {});
}
Он фильтрует ключ, который следует удалить, а затем создает новый объект из остальных ключей и исходного объекта. Идея украдена из программы Tyler McGinnes awesome reactjs.
JSBin
Ответ 7
function dissoc(key, obj) {
let copy = Object.assign({}, obj)
delete copy[key]
return copy
}
Кроме того, если вы ищете функциональный инструментарий программирования, посмотрите на Ramda.
Ответ 8
Вы можете использовать Immutability helper для удаления атрибута, в вашем случае:
import update from 'immutability-helper';
const updatedState = update(state, {
c: {
$unset: ['y']
}
});
Ответ 9
Это легко с Immutable.js:
const newState = state.deleteIn(['c', 'y']);
описание deleteIn()
Ответ 10
Проблема, с которой вы столкнулись, заключается в том, что вы не глубоко копируете свое первоначальное состояние. Итак, у вас есть мелкая копия.
Вы можете использовать спред оператора
const newState = { ...state, c: { ...state.c } };
delete newState.c.y
Или следуя тому же коду
let newState = Object.assign({}, state, { c: Object.assign({}, state.c) });
delete newState.c.y
Ответ 11
Обычно я использую
Object.assign({}, existingState, {propToRemove: undefined})
Я понимаю, что это фактически не снимает свойство, но почти для всех целей 1 его функционально эквивалентно. Синтаксис для этого намного проще, чем альтернативы, которые, как я считаю, являются довольно хорошим компромиссом.
1 Если вы используете hasOwnProperty()
, вам нужно будет использовать более сложное решение.
Ответ 12
Я использую этот шаблон
const newState = Object.assign({}, state);
delete newState.show;
return newState;
но в книге я увидел другой образец
return Object.assign({}, state, { name: undefined } )
Ответ 13
Как уже намечено в некоторых ответах, это потому, что вы пытаетесь изменить вложенное состояние, т.е. на один уровень глубже. Каноническим решением было бы добавить редуктор на уровне состояния 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});
Ответ 14
полезность ;))
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;
}
};
Ответ 15
Начиная с 2019 года, другим вариантом является использование метода Object.fromEntries
. Он достиг стадии 4.
const newC = Object.fromEntries(
Object.entries(state.c).filter(([key]) => key != 'y')
)
const newState = {...state, c: newC}
Приятно то, что он прекрасно обрабатывает целочисленные ключи.
Ответ 16
Вы можете использовать React immutability helpers, чтобы перезаписать либо весь c
, если у вас есть это, либо для установки значения y
до undefined
или null
. Это зависит от вашего варианта использования, который может работать лучше всего:
const newState = update(state, {
c: {$set: {
x: '42',
}},
});
или
const newState2 = update(state, {
c: {
y: {$set: undefined},
},
});