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.
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.
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.
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.
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.
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).
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With