Redux: Never Mutate State

Redux: Never Mutate State
0

#1

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

#3
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.


#4

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

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


#5

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

#6

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

#7

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.


#8

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


#9

@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


#10

@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];

#11

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.


#13

‘concat’ returns a new array.


#14

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?


#15

The push method changes/mutates the array on which it is called. I would need to see exactly how you are where and how you are calling the push method to tell you if push could be used in some way to solve the challenge.


#16

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


#17

I had a feeling that was what you were trying. Don’t forget the push method actually returns a value. Do you remember what value the push method returns? Hint: It is not an array.


#18

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: