Redux: Never Mutate State

This code doesn’t pass the last test which is “Dispatching an action of type ADD_TO_DO on the Redux store should add a todo item and should NOT mutate state.”?

const ADD_TO_DO = 'ADD_TO_DO';

// A list of strings representing tasks to do:
const todos = [
  'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code',
];

const immutableReducer = (state = todos, action) => {
  switch(action.type) {
    case ADD_TO_DO:
      // don't mutate state here or the tests will fail
      let a = state.map((item)=>item);
      a.push(action.todo);
      return a;
    default:
      return state;
  }
};

// an example todo argument would be 'Learn React',
const addToDo = (todo) => {
  return {
    type: ADD_TO_DO,
    todo:'Learn React'
  }
}

const store = Redux.createStore(immutableReducer);
const addToDo = (todo) => {
  return {
    type: ADD_TO_DO,
    todo:'Learn React'
  }
}

You’re hard coding todo's value. It should just be whatever is passed into the action creator.

1 Like

To make sure not to mutate , you have to use old-fashion style

let a = JSON.parse(JSON.stringify(state));

This is the code that got me passing the exercise:

const immutableReducer = (state = todos, action) => {
  switch(action.type) {
    case ADD_TO_DO:
      // don't mutate state here or the tests will fail
      return state.concat(action.todo);
    default:
      return state;
  }
};
4 Likes

Hi, I have the same problem, the code it’s seem OK, don’t mutate state and return a new array with the new todo item but doesn’t pass the last test.

const ADD_TO_DO = 'ADD_TO_DO';

// A list of strings representing tasks to do:
const todos = [
  'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code',
];

const immutableReducer = (state = todos, action) => {
  switch(action.type) {
    case ADD_TO_DO:
      // don't mutate state here or the tests will fail
      let a = state.concat(action.todo)
      console.log(state) // ["Go to the store", "Clean the house", "Cook dinner", "Learn to code"]
      console.log(a) // ["Go to the store", "Clean the house", "Cook dinner", "Learn to code", "Learn React"]
      return a;
    default:
      return state;
  }
};

// an example todo argument would be 'Learn React',
const addToDo = (todo) => {
  return {
    type: ADD_TO_DO,
    todo: 'Learn React'
  }
}

const store = Redux.createStore(immutableReducer);
1 Like

To make it in the ES6 style, just add the code below as the return statement in ADD_TO_DO case.

return [...state, action.todo];

Thank you.

11 Likes

Thanks, I solved, the problem was the return object must be:

const addToDo = (todo) => {
  return {
    type: 'ADD_TO_DO',
    todo
  }
}

instead of:

const addToDo = (todo) => {
  return {
    type: 'ADD_TO_DO',
    todo: 'Learn React'
  }
}

It can be solved with concat or spread operator

2 Likes

@SoulTrain just a question regarding your solution. Not criticizing or anything, it worked for me as well, but I am just trying to understand why. Doesn’t the concat method add to what’s already initialized, i.e ‘todos’. That way it will be returning an array with the new elements on top of the existing ones and not a new array with the new elements alone…
the Array.map and Array.slice functions are also non-mutating. However, I could’t get those to work for some reason.
Please if anyone can answer me it would be awesome. Really need to grasp this.

Thanks

@stevenYouhana .map(), .slice(), and .concat() can each be used. I was able to get each of them to pass the tests using the code below:

.concat()

return state.concat(action.todo);

.map()

const newState = state.map(todo => todo);
newState.push(action.todo);
return newState;

.slice()

const newState = state.slice(0);
newState.push(action.todo);
return newState;

es6 magic

return [...state, action.todo];
3 Likes

Thanks I understand now. I was intentionally trying to return an array only with new items, i.e. not pushing them to the origin items.

‘concat’ returns a new array.

hi, i noticed we can’t use .push? why is that?
i managed to pass my code with
let newArr =[…state];
return newArr.concat(action.todo)
but when i use .push(action.todo ) it says ‘Dispatching an action of type ADD_TO_DO on the Redux store should add a todo item and should NOT mutate state’. i don’t get how it’s mutating the state when i defined a let newArr
inside and used the spread operator to store the state. can someone explain?

Hi, thanks for the reply,
I was using the same method I used to pass the challenge and I defined the new array inside the case of reducer, just before the return statement like below

[spoiler]```
const ADD_TO_DO = ‘ADD_TO_DO’;

// A list of strings representing tasks to do:
const todos = [
‘Go to the store’,
‘Clean the house’,
‘Cook dinner’,
‘Learn to code’,
];

const immutableReducer = (state = todos, action) => {
switch(action.type) {
case ADD_TO_DO:
// don’t mutate state here or the tests will fail
let newArray = […state];
return newArray.push(action.todo) // " This is the line where i used concat instead of push to pass the code"
default:
return state;
}
};

// an example todo argument would be ‘Learn React’,
const addToDo = (todo) => {
return {
type: ADD_TO_DO,
todo
}
}

const store = Redux.createStore(immutableReducer);

oh i just checked the mdn it returns length of the array. Thank you!
i thought it returned the entire array but i was mistaken :smiley:

1 Like

I was tearing my hair out for over an hour on this one… the solution was obvious, but what I did was to test my solution by adding store.dispatch(addToDo(‘Get something to eat’)); to test what I was doing. Seemed legit. But when I tested, it kept failing…

=====================================================================

const ADD_TO_DO = ‘ADD_TO_DO’;

// A list of strings representing tasks to do:
const todos = [
‘Go to the store’,
‘Clean the house’,
‘Cook dinner’,
‘Learn to code’,
];

const immutableReducer = (state = todos, action) => {
switch(action.type) {

case ADD_TO_DO:
  return state.slice().concat(action.todo);
default:
  return state;

}
};

// an example todo argument would be ‘Learn React’,
const addToDo = (todo) => {
return {
type: ADD_TO_DO,
todo
}
}

const store = Redux.createStore(immutableReducer);
store.dispatch(addToDo(‘Get some food’)); // this shouldn’t cause the test to fail!!

=====================================================================

Bottom line - there’s a problem (imho) with testing if you can’t actually run it the way you’d think you should be able to. Unless I’m missing something here??? Like I said, take the last line out and the test passes just fine…

Okay I’ve just finished with this exercise, but I have a question about the todo action we are adding. In the following snippet of code, FCC gives us:

const addToDo = (todo) => {
  return {
    type: ADD_TO_DO,
    todo
  }
}

Perhaps we aren’t covering this yet (probably in React & Redux is my guess), but where would someone actually write or put what ‘todo’ they wanted to add to their list? Are we just to assume that is this particular exercise, we’re adding a blank?

Why do the comments imply that the todo has to be assigned a value or something? Weird.

In the test there is a store.dispatch (addToDo(’__ TEST__TO__DO__’)), that’s where the value comes from.

1 Like

Thanks for this reply. I was hard coding the todo as well. Changed it and passed!

TO Moderator: I feel this should be marked as a bug. We are taught that an action creator returns an object.

This is the object as written in the addToDo action creator:

  return {
    type: ADD_TO_DO,
    todo
  }

Yes, this is an object written with shorthand key/value pairs, but should it be used in a training exercise?

1 Like