DEV Community

Randy Rivera
Randy Rivera

Posted on • Edited on

Redux: Never Mutate State

  • Today FreeCodeCamp will be giving us a lesson about enforcing the key [principle of state immutability in Redux. Which means that you never modify state directly. Instead you return a new copy of state.
  • Redux does not actively enforce state immutability in its store or reducers. Remember strings and numbers are primitive values and are immutable by nature. In other words, 3 is always 3. You cannot change the value of the number 3. An array or object, however, is mutable.
  • Code:
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 return default: return state; } }; const addToDo = (todo) => { return { type: ADD_TO_DO, todo } } const store = Redux.createStore(immutableReducer); 
Enter fullscreen mode Exit fullscreen mode
  • Above we have a store and reducer in the code editor for managing to-do items. You have to finish writing the ADD_TO_DO case in the reducer to append a new-to-do to the state. You have tp find a way to return a new array with the item from action.todo appended to the end.

  • Answer:

const ADD_TO_DO = 'ADD_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: let newList = [...todos] newList.push(action.todo) return newList; default: return state; } }; const addToDo = (todo) => { return { type: ADD_TO_DO, todo } } const store = Redux.createStore(immutableReducer); store.dispatch(addToDo('PS5 Time'))**** console.log(store.getState()) //  [ 'Go to the store', 'Clean the house', 'Cook dinner', 'Learn to code', 'PS5 Time' ] console.log(todos) //  [ 'Go to the store', 'Clean the house', 'Cook dinner', 'Learn to code' ] 
Enter fullscreen mode Exit fullscreen mode

Use the Spread Operator on Arrays

  • (ES6) To help enforce state immutability in Redux is the spread operator: .... which I used in the lesson above which produces a new array from an existing array.
  • To clone an array but add additional values in the new array, you could write [...myArray, 'new value']. This would return a new array composed of the values in myArray and the string new value as the last value.
  • It's important to know that it only makes a shallow copy of the array. It only provides immutable array operations for one-dimensional arrays.

  • Code:

const immutableReducer = (state = ['Do not mutate state!'], action) => { switch(action.type) { case 'ADD_TO_DO': // 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); 
Enter fullscreen mode Exit fullscreen mode
  • Answer:
const immutableReducer = (state = ['Do not mutate state!'], action) => { switch(action.type) { case 'ADD_TO_DO': let newArray = [...state, action.todo] return newArray default: return state; } }; 
Enter fullscreen mode Exit fullscreen mode

Remove an Item from an Array

  • Now we gotta practice removing items from an array. Other useful JavaScript methods include slice() and concat().

  • The reducer and action creator were modified to remove an item from an array based on the index of the item. Let's Finish writing the reducer so a new state array is returned with the item at the specific index removed.

*Code:

const immutableReducer = (state = [0,1,2,3,4,5], action) => { switch(action.type) { case 'REMOVE_ITEM': // Don't mutate state here or the tests will fail return default: return state; } }; const removeItem = (index) => { return { type: 'REMOVE_ITEM', index } } const store = Redux.createStore(immutableReducer); 
Enter fullscreen mode Exit fullscreen mode
  • Answer:
const immutableReducer = (state = [0,1,2,3,4,5], action) => { switch(action.type) { case 'REMOVE_ITEM': let a = state.slice(0, action.index) let b = state.slice(action.index + 1) return a.concat(b) default: return state; } }; const removeItem = (index) => { return { type: 'REMOVE_ITEM', index } } const store = Redux.createStore(immutableReducer); 
Enter fullscreen mode Exit fullscreen mode

Larson, Q., 2019. Frontend Development Libraries. [online] Freecodecamp.org. Available at: https://www.freecodecamp.org/learn/front-end-development-libraries/redux

Top comments (0)