Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Update single value in item array | react redux

Tags:

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.

like image 240
Filth Avatar asked Dec 07 '16 13:12

Filth


People also ask

How do I update Redux values?

First, we will find the index of the item in the array using findIndex() . Then we make a copy of the todos array from the state. Then we can change the value in the new array using the index value we got from findIndex() . Lastly, in the return, we reassign todos to that new array.

How do you update an element in an array?

To update all the elements of an array, call the forEach() method on the array, passing it a function. The function gets called for each element in the array and allows us to update the array's values. Copied! const arr = ['zero', 'one', 'two']; arr.

How do you update an object in react Redux?

Redux: Update an Object When you want to update the top-level properties in the Redux state object, copy the existing state with ... state and then list out the properties you want to change, with their new values.

Can we update state in reducer?

The state is updated and managed by reducers. Reducers always have to return something even if it's null ; they should never return undefined . If a reducer's state is an object, you must always return a new object instead of editing the object in place.


2 Answers

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

like image 152
TomW Avatar answered Sep 29 '22 04:09

TomW


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);
like image 35
Mayank Shukla Avatar answered Sep 29 '22 03:09

Mayank Shukla