Javascript 更新项目数组中的单个值| react redux

Javascript 更新项目数组中的单个值| react redux,javascript,reactjs,redux,state,reducers,Javascript,Reactjs,Redux,State,Reducers,我有一个待办事项列表,如果用户单击“完成”,我想将数组中该项的状态设置为“完成” 以下是我的行动: export function completeTodo(id) { return { type: "COMPLETE_TASK", completed: true, id } } 这是我的减速机: case "COMPLETE_TASK": { return {...state,

我有一个待办事项列表,如果用户单击“完成”,我想将数组中该项的状态设置为“完成”

以下是我的行动:

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

如您所见,我只想将completed值设置为true

您需要转换todos数组以更新相应的项。Array.map是执行此操作的最简单方法:

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
        ) 
    };
有一些库可以帮助您进行这种深度状态更新。您可以在此处找到此类库的列表:

就我个人而言,我使用ImmutableJS()解决了这个问题,它使用了
updateIn
setIn
方法(对于具有大量键的大型对象和数组,它们比普通对象和数组更有效,但对于小型对象,它们更慢)

新状态不再具有与上的todo项关联的文本 所选项目和ID不再存在,这是因为我 覆盖状态并忽略以前的属性

是的,因为在每次更新过程中,您只分配了一个新数组,其中
已完成
,并且该数组不包含任何以前的值。因此,更新后的数组将没有以前的数据。这就是为什么文本和id在更新后不存在的原因

解决方案:

1-用于查找正确的元素,然后更新值,如下所示:

case "COMPLETE_TASK":
    return {
        ...state,
        todos: state.todos.map(todo => 
            todo.id === action.id ? { ...todo, completed: action.completed } : todo
        ) 
    };
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}
2-用于查找特定对象的索引,然后更新该索引,如下所示:

case "COMPLETE_TASK":
    return {
        ...state,
        todos: state.todos.map(todo => 
            todo.id === action.id ? { ...todo, completed: action.completed } : todo
        ) 
    };
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={
答:1,,
arr:[
{text:1,id:1,completed:true},
{text:2,id:2,completed:false}
]
}
log('old values',JSON.stringify(state));
//更新值
让newState={
……国家,
arr:[{已完成:true}]
}

log('newState=',newState)React中最重要的设计原则之一是“不改变状态”。如果要更改数组中的数据,则需要使用更改后的值创建一个新数组

例如,我在state中有一个结果数组。最初,我只是将构造函数中的每个索引的值设置为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         
};
稍后,我想更新数组中的一个值。但我不会在state对象中更改它。对于ES6,我们可以使用扩展运算符。数组切片方法返回一个新数组,它不会更改现有数组

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)
});

感谢您对TomW的帮助,并感谢您参考有关深度状态更新的库。帮了大忙!我知道你用的是单行if语句,不过我觉得读起来很难。我知道如果有if语句,减缩器就会爆炸。您是否能够解释这里发生了什么,以及编写此逻辑的可能的另一种方式?当您没有从每个分支返回状态时,减缩器会“爆炸”——
如果
语句没有问题,您只需要确保“返回状态;”在
if
之后。如果您查看下面的TOGGLE_TODO处理程序:您将看到一个与我的非常类似的示例,它使用
If
语句而不是
?:
-
?:
比If语句稍微好一点,因为它强制您从每个分支返回一个值。