7

I am creating a todolist with react and redux and when I update redux state array it doesn't re-render, My state is actually an array which contains objects, something like this:

[{index:1,value:'item1',done:false}, {index:2,value:'item2',done:false}]

What i want to do is on click i want to toggle the value of done to 'true', But somehow I am unable to do that.

This is what I was doing in my reducer:

list.map((item)=>{
     if(item.index===index){
         item.done=!item.done;
         return [...state,list]
     }

But it doesn't re-render even though done keeps changing on clicking the toggle button.

It seems that somehow I am mutating the state. please tell me where am I going wrong and what should I do instead.

Could you give examples of something similar. I can update state of simple arrays correctly, but doing it for an array containing objects , is what's confusing me. so, could you give examples of that?

Here's the full reducer code:

export default function todoApp(state=[],action){
    switch(action.type){
        case 'ADD_TODO':
            return [...state,action.item];
        case 'TOGGLE_TODOS':
            const index = action.index;
            const list = state;

            list.map((item)=>{
            if(item.index===index){
                item.done=!item.done;

            }
            return [...state,list];
            });
        default:
          return state;    
    } 
}

3 Answers 3

11

It seems that somehow I am mutating the state.

Correct you are mutating the state, because in js, variable always get reference of object/array. In your case item will have the reference of each object of the array and you are directly mutating the value of item.done.

Another issue is you are not returning the final object properly, also you need to return value for each map iteration otherwise by default it will return undefined.

Write it like this:

case "TOGGLE_TODOS": 
    return list.map((item) => (
        item.index===index? {...item, done: !item.done}: item
    ))

Or:

case 'TOGGLE_TODOS':
    const index = action.index;
    const newState = [ ...state ];
    newState[index] = { ...state[index], done: !newState[index].done };
    return newState;

Full Code:

export default function todoApp(state=[], action){
    switch(action.type){
        case 'ADD_TODO':
            return [...state, action.item];
        case 'TOGGLE_TODOS':
            const index = action.index;
            return state.map((item) => (
                item.index===index? {...item, done: !item.done}: item
            ))
        default:
            return state;    
    }
}

Check this snippet:

let list = [
    {done:true, index:0},
    {done:false, index:1},
    {done: true, index:2}
  ]

let index = 1;

let newList = list.map(item => (
    item.index===index? {...item, done: !item.done}: item
))

console.log('newList = ', newList);

Sign up to request clarification or add additional context in comments.

7 Comments

could you give an example of array containing objects. because that's what's confusing me. I want to update just a property of the object and I'd like to know how to do that.
@faraz check the updated answer, let me know if have any doubt in that :)
so, in your code above. would you return state like this --> return {...obj,newObj} ??
where you are getting confused, can you should full state code where you tried and what i will help you.
updated full reducer code - what I was doing originally. it was changing the value of done to true and false accordingly but not rerendering.
|
1

Check out the documentation for Array.prototype.Map.

The callback function should return element of the new Array. Try this:

return list.map(item => {
  if (item.index === index) {
    return {
      done: !item.done
      ...item,
    }

  return item;
});

1 Comment

could you give an example of array containing objects. because that's what's confusing me. I want to update just a property of the object and I'd like to know how to do that.
0

Although there already exist two correct answers, I'd like to throw lodash/fp in here as well, which is a bit more dense and readable and also doesn't mutate

import { set } from 'lodash/fp'

return list.map(item => {
    if (item.index === index) {
        return set('done', !item.done, item)
    }
    return item
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.