简体   繁体   English

更新项目数组中的单个值 反应redux

[英]Update single value in item array | react redux

I have a todo list and want to set the state of that item in the array to "complete" if the user clicks on "complete". 我有一个待办事项列表,如果用户单击“完成”,希望将数组中该项目的状态设置为“完成”。

Here is my action: 这是我的动作:

export function completeTodo(id) {
    return {
        type: "COMPLETE_TASK",
        completed: true,
        id
    }
}

Here is my reducer: 这是我的减速器:

case "COMPLETE_TASK": {
             return {...state,
                todos: [{
                    completed: action.completed
                }]
             }
        }

The problem I'm having is the new state does no longer have the text associated of that todo item on the selected item and the ID is no longer there. 我遇到的问题是新状态在所选项目上不再具有与该待办事项相关的文本,并且ID不再存在。 Is this because I am overwriting the state and ignoring the previous properties? 这是因为我要覆盖状态并忽略以前的属性吗? My object item onload looks like this: 我的对象项onload看起来像这样:

Objecttodos: Array[1]
    0: Object
        completed: false
        id: 0
        text: "Initial todo"
    __proto__: Object
    length: 1
    __proto__: Array[0]
    __proto__: Object

As you can see, all I want to do is set the completed value to true. 如您所见,我要做的就是将完成值设置为true。

You need to transform your todos array to have the appropriate item updated. 您需要转换待办事项数组以更新适当的项目。 Array.map is the simplest way to do this: 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
        ) 
    };

There are libraries to help you with this kind of deep state update. 有一些库可帮助您进行这种深度状态更新。 You can find a list of such libraries here: https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities 您可以在此处找到此类库的列表: https : //github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities

Personally, I use ImmutableJS ( https://facebook.github.io/immutable-js/ ) which solves the issue with its updateIn and setIn methods (which are more efficient than normal objects and arrays for large objects with lots of keys and for arrays, but slower for small ones). 就我个人而言,我使用ImmutableJS( https://facebook.github.io/immutable-js/ )来解决其updateInsetIn方法的问题(对于具有大量键的大型对象而言,它们比普通对象和数组更有效)阵列,但对于小型阵列则较慢)。

New state does no longer have the text associated of that todo item on the selected item and the ID is no longer there, Is this because I am overwriting the state and ignoring the previous properties? 新状态不再与所选项目上的待办事项相关联,并且ID不再存在。这是因为我要覆盖状态并忽略先前的属性吗?

Yes, because during each update you are assigning a new array with only one key completed , and that array doesn't contain any previous values. 是的,因为在每次更新过程中,您将分配一个 completed 一个键的新数组,并且该数组不包含任何先前的值。 So after update array will have no previous data. 所以更新后的数组将没有以前的数据。 That's why text and id's are not there after update. 这就是为什么文本和ID在更新后不存在的原因。

Solutions: 解决方案:

1- Use array.map to find the correct element then update the value, Like this: 1-使用array.map查找正确的元素,然后更新值,如下所示:

case "COMPLETE_TASK":
    return {
        ...state,
        todos: state.todos.map(todo => 
            todo.id === action.id ? { ...todo, completed: action.completed } : todo
        ) 
    };

2- Use array.findIndex to find the index of that particular object then update that, Like this: 2-使用array.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}

Check this snippet you will get a better idea about the mistake you are doing: 查看以下代码片段,您将对自己所犯的错误有一个更好的了解:

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

One of the seminal design principles in React is "Don't mutate state." React中最重要的设计原则之一是“不要改变状态”。 If you want to change data in an array, you want to create a new array with the changed value(s). 如果要更改数组中的数据,则要使用更改后的值创建一个新数组。

For example, I have an array of results in state. 例如,我在状态中有一系列结果。 Initially I'm just setting values to 0 for each index in my constructor. 最初,我只是将构造函数中每个索引的值设置为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         
};

Later on, I want to update a value in the array. 稍后,我想更新数组中的值。 But I'm not changing it in the state object. 但是我没有在状态对象中更改它。 With ES6, we can use the spread operator. 使用ES6,我们可以使用传播运算符。 The array slice method returns a new array, it will not change the existing array. 数组切片方法返回一个新数组,它将不会更改现有数组。

updateArray = (list, index,score) => {
   // updates the results array without mutating it
      return [
        ...list.slice(0, index),
        list[index][1] = score,
       ...list.slice(index + 1)
     ];
};

When I want to update an item in the array, I call updateArray and set the state in one go: 当我想更新数组中的项目时,我调用updateArray并一次性设置状态:

this.setState({
        index:newIndex, 
        question:this.props.test[0].questions[newIndex],
        results:this.updateArray(this.state.results, index, score)
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM