Redux - Never Mutate State

Tell us what’s happening:
Hello, I’m trying to not mutate state but I keep getting this alert when I run the test : “Dispatching an action of type ADD_TO_DO on the Redux store should add a todo item and should NOT mutate state.”
Someone can tell me what’s wrong please ?

  **Your code so far**
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 {
type:  state.concat([
'Go to the store', 
'Clean the house', 
'Cook dinner',
'Learn to code',])
  }
    // Don't mutate state here or the tests will fail
    return
  default:
    return state;
}
};

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

const store = Redux.createStore(immutableReducer);
  **Your browser information:**

User Agent is: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.1 Safari/605.1.15

Challenge: Redux - Never Mutate State

Link to the challenge:

Read the whole error:

Dispatching an action of type ADD_TO_DO on the Redux store should add a todo item and should NOT mutate state.

It’s telling you:

  1. should add a todo item
  2. should NOT mutate state

The problem is that you failed the first part, not the second. You did not mutate the state.

What you did was fail to add the correct todo and created a new shape of state.

Put this at the bottom of the code so you can see what is happening to the state in the test:

store.subscribe(() => console.log(store.getState()))

You start out with this shape of state:

[
  'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code',
]

And you end up with this:

{
  type: [ 
    'Go to the store',
    'Clean the house',
    'Cook dinner',
    'Learn to code',
    'Go to the store',
    'Clean the house',
    'Cook dinner',
    'Learn to code',
  ] 
}

That is not correct. You’re on the right track, you just need to make sure that:

  1. You add the correct todo, the one that was passed in on your action.
  2. Return the correct shape of state. Remember that you are returning what you want the new state to be.

Give that a shot. If you still have trouble, check back.

Don’t get frustrated - redux is weird. Yes, it’s really cool and powerful once you get it, but it’s weird until you do.

1 Like

I tried this :slight_smile: switch(action.type)
But It does not work. I am a little bit confused with the little question ‘You add the correct todo’, since I think I did {

  case ADD_TO_DO:
   return {
     ...state,
     todos: [...state.todos, action.todo]
   }
   /*return state.concat([
'Go to the store', 
'Clean the house', 
'Cook dinner',
'Learn to code',])*/
   
     // Don't mutate state here or the tests will fail
     return
   default:
     return state;
 }```

OK, I think we’re getting closer - we’re adding the right todo now.

The question is “what is state?”

We define the initial todos here:

const todos = [
  'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code',
];

Then we assign that as the initial state here:

const immutableReducer = (state = todos, action) => {

By giving that as the default for the state in the parameters, we are saying “use this as the initial state”. In fact many people will just call it that, not “todos”.

So, our state is an array of strings. You can confirm that by putting in a log statement:

  case ADD_TO_DO:
    console.log('state before ADD:', state)
   return {
     ...state,
     todos: [...state.todos, action.todo]
   }

But you aren’t returning an array of strings. You’re trying to return an object with a property “todos” that is an array of strings. That would only make sense if we defined our initial state as:

const initialState = {
  todos: [
    'Go to the store',
    'Clean the house',
    'Cook dinner',
    'Learn to code',
  ]
}

and then used that as our initial state in the reducer.

Why not do it that way? Well, we could have. If there were more data in this reducer, it would be a good idea. But as this reducer is defined, “state” is just an array of strings.

Fix that and you’re good.

Hint: You’re basically just deleting some characters, it’s simpler than what you have.

1 Like

Thank you for your help, I passed it. But I have one more question.

If the solution is : return […state, action.todo], and we want to add more arrays.

example : imagine we want to add newArr and newNewArr.

And we don’t want to mutate it.

So we have to do this : return […state, …todo, action.newArr] and return […state, …todo, …newArr, action.newNewArr] ??? I am not sure if it will. But I want to know in case we have a lot of arrays.

Thank you

I’m not sure what you mean. This action is to add a todo. If you want to add several todos in the form or an array, one way to do it would be something like:

  case ADD_TODOS:
   return [...state, ...action.todos]

That would work. I’m not sure what the application would be beyond that. When would you add a single todo and a list of todos. But whatever, if you can dream it, there’s a way to do it.

I’m not sure I understand: return […state, …todo, action.newArr].

What is todo in that? Where is that coming from if it’s not on the action? I definitely don’t understand return […state, …todo, …newArr, action.newNewArr]?

You realize that this can be used more than once, right?

Add this to the bottom of your code:


console.log('*** start')
console.log(store.getState())

console.log('*** add something')
store.dispatch(addToDo('feed the cat'))
console.log(store.getState())

console.log('*** add something else')
store.dispatch(addToDo('take out the trash'))
console.log(store.getState())

console.log('*** add another something else')
store.dispatch(addToDo('buy milk'))
console.log(store.getState())

and see the results

*** start
[ 'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code' ]
*** add something
[ 'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code',
  'feed the cat' ]
*** add something else
[ 'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code',
  'feed the cat',
  'take out the trash' ]
*** add another something else
[ 'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code',
  'feed the cat',
  'take out the trash',
  'buy milk' ]

But yeah, whatever you need to do, you can add a make an action for it. For a todo add, I would expect cases for removing a todo, editing a todo, clearing the whole list, etc. Those would just have their own action creators that have their own action type and their own cases in the reducer.

1 Like

Thank you for your time, I appreciate it. I misunderstood something about what we could expect from a todo add. Now I get it. Thank you

1 Like