Обновить одно значение в массиве элементов / react redux
у меня есть список задач и я хочу установить состояние этого элемента в массиве "завершить", если пользователь нажимает"завершить".
вот мои действия:
export function completeTodo(id) {
return {
type: "COMPLETE_TASK",
completed: true,
id
}
}
вот мой редуктор:
case "COMPLETE_TASK": {
return {...state,
todos: [{
completed: action.completed
}]
}
}
проблема, которую я имею-это новое государство уже не существует текста, связанного этой todo пункта на выбранный товар и ID уже не существует. Это потому, что я перезаписываю состояние и игнорирую предыдущие свойства? Мой объект события onload выглядит так:
Objecttodos: Array[1]
0: Object
completed: false
id: 0
text: "Initial todo"
__proto__: Object
length: 1
__proto__: Array[0]
__proto__: Object
как вы можете видеть, все, что я хочу сделать, это установить завершенное значение true.
3 ответов
вам нужно преобразовать массив todos, чтобы обновить соответствующий элемент. Матрица.карта-самый простой способ сделать это:
case "COMPLETE_TASK":
return {
...state,
todos: state.todos.map(todo => todo.id === action.id ?
// transform the one with a matching id
{ ...todo, completed: action.completed } :
// otherwise return original todo
todo
)
};
есть библиотеки, которые помогут вам с таким глубоким обновлением состояния. Список таких библиотек можно найти здесь: https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities
лично я использую ImmutableJS (https://facebook.github.io/immutable-js/), который решает проблему с его updateIn
и setIn
методы (которые более эффективны, чем обычные объекты и массивы для больших объектов с большим количеством ключей и для массивов, но медленнее для небольших).
один из основополагающих принципов дизайна в React - " не мутировать состояние."Если вы хотите изменить данные в массиве, вы хотите создать новый массив с измененным значением(значениями).
например, у меня есть массив результатов в состоянии. Первоначально я просто устанавливаю значения 0 для каждого индекса в моем конструкторе.
this.state = {
index:0,
question: this.props.test[0].questions[0],
results:[[0,0],[1,0],[2,0],[3,0],[4,0],[5,0]],
complete: false
};
позже, я хочу, чтобы обновить значение в массиве. Но я не меняю его в государственном объекте. С ES6 мы можем использовать оператор распространения. Матрица метод slice возвращает новый массив, он не будет изменять существующий массив.
updateArray = (list, index,score) => {
// updates the results array without mutating it
return [
...list.slice(0, index),
list[index][1] = score,
...list.slice(index + 1)
];
};
когда я хочу обновить элемент в массиве, я вызываю updateArray и устанавливаю состояние за один раз:
this.setState({
index:newIndex,
question:this.props.test[0].questions[newIndex],
results:this.updateArray(this.state.results, index, score)
});
новое состояние больше не имеет текста, связанного с этим элементом todo на выбранный элемент и идентификатор больше не существует, это потому, что я перезапись состояния и игнорирование предыдущих свойств?
Да, потому что во время каждого обновления вы назначаете новый массив с единственный ключ completed
и этот массив не содержит каких-либо предыдущих значений. Поэтому после обновления массив не будет иметь предыдущих данных. Вот почему текст и ID не там после обновления.
решения:
1- использовать массив.карта чтобы найти правильный элемент, обновите значение, например:
case "COMPLETE_TASK":
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.id ? { ...todo, completed: action.completed } : todo
)
};
2 - Использовать массив.findIndex чтобы найти индекс этого конкретного объекта, обновите его, например:
case "COMPLETE_TASK":
let index = state.todos.findIndex(todo => todo.id === action.id);
let todos = [...state.todos];
todos[index] = {...todos[index], completed: action.completed};
return {...state, todos}
проверьте этот фрагмент, вы получите лучшее представление об ошибке, которую вы делаете:
let state = {
a: 1,
arr: [
{text:1, id:1, completed: true},
{text:2, id:2, completed: false}
]
}
console.log('old values', JSON.stringify(state));
// updating the values
let newState = {
...state,
arr: [{completed: true}]
}
console.log('new state = ', newState);