Update single value in item array | react redux

北城以北 提交于 2019-12-17 08:53:37

问题


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. Is this because I am overwriting the state and ignoring the previous properties? My object item onload looks like this:

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.


回答1:


You need to transform your todos array to have the appropriate item updated. Array.map is the simplest way to do this:

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

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




回答2:


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?

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. So after update array will have no previous data. That's why text and id's are not there after update.

Solutions:

1- Use array.map to find the correct element then update the value, Like this:

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:

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



回答3:


One of the seminal design principles in React is "Don't mutate state." 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.

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. 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:

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


来源:https://stackoverflow.com/questions/41018605/update-single-value-in-item-array-react-redux

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!