02Geek HTML5 and JavaScript, TypeScript, React, Flash, ActionScript online School
Previous VideoNext Video

Deep Freezing Objects in Redux

Deep Freezing Objects in Redux

In this tutorial, we will explore the concept of deep freezing objects in Redux, understand why it's important, and how it can help maintain state immutability in our applications. By the end of this tutorial, you will have a solid grasp on using deep freeze to prevent unwanted state changes, which helps maintain predictable and error-free Redux operations.

Why Deep Freeze?

The core idea behind Redux is that the state should be immutable. This means that every time the state changes, a new version of it is created rather than modifying the existing one. This approach makes it easy to track changes over time, enabling features like undo/redo and simplifying debugging by keeping a clear history of changes.

When working inside a reducer, we do not want to modify the current state directly, as it can lead to unpredictable behavior and bugs. Deep freezing helps to ensure that our state remains immutable, preventing any accidental changes to the existing state while building the new one.

Using Deep Freeze

1. The Problem of Direct State Mutation

When you modify the state directly inside a reducer, it can create unexpected behaviors, especially since the reducer is responsible for aggregating changes and producing a new state. Direct mutation could lead to overwriting changes before the new state is finalized, resulting in inconsistent or incorrect data.

Here is an example of how direct state mutation might occur:

const boardReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'BOARD_CHANGE':
      // Incorrect way: Directly mutating the state
      state.color1 = action.color1;
      state.color2 = action.color2;
      return state;
    default:
      return state;
  }
};

In the above example, we're modifying state.color1 and state.color2 directly, which violates Redux's immutability principle.

2. Applying Deep Freeze

To prevent state mutation, we can use the deep-freeze library. This library recursively freezes the state object, ensuring that no properties of the state can be modified. This is especially useful during development and testing to catch any direct mutations.

Installing Deep Freeze

First, we need to install the deep-freeze library as a development dependency:

npm install deep-freeze --save-dev

Importing and Using Deep Freeze

Once installed, we can use deep-freeze to enforce immutability in our reducers.

import deepFreeze from 'deep-freeze';

const boardReducer = (state = initialState, action) => {
  deepFreeze(state); // Ensure the state is frozen before making changes

  switch (action.type) {
    case 'BOARD_COLOR_CHANGE':
      // Correct way: Creating a new state object
      return {
        ...state,
        color1: action.color1,
        color2: action.color2,
      };
    default:
      return state;
  }
};

In this example, deepFreeze(state) is used to ensure that the state object cannot be mutated. If any code attempts to modify state directly, an error will be thrown, helping us catch bugs early during development.

3. Testing with Deep Freeze

Testing reducers is an essential part of maintaining a reliable application. By using deep-freeze, we can write tests to ensure our state remains immutable.

Here's an example of how to write a test that uses deep-freeze:

import deepFreeze from 'deep-freeze';

it('should change board colors correctly', () => {
  const initialState = {
    color1: 'black',
    color2: 'white',
  };

  deepFreeze(initialState); // Freeze the initial state to prevent mutations

  const action = {
    type: 'BOARD_COLOR_CHANGE',
    color1: 'red',
    color2: 'yellow',
  };

  const newState = boardReducer(initialState, action);

  expect(newState).toEqual({
    color1: 'red',
    color2: 'yellow',
  });
});

In this test, we freeze the initialState before passing it to the reducer. This helps ensure that the reducer does not mutate the existing state and instead returns a new state object.

Conclusion

In this tutorial, we covered:

  • Why Deep Freeze is Important: To prevent direct state mutations and ensure immutability in Redux.
  • Using Deep Freeze in Reducers: How to install and apply deep-freeze to ensure the state remains immutable.
  • Testing Immutability: Using deep-freeze in tests to validate that state changes are handled correctly without mutations.

By applying deep freezing, we can maintain a more predictable and robust Redux application. It helps catch potential issues early in the development phase and reinforces the best practices of immutability in Redux.

In the next lecture, we will continue building out our checkboard functionality and explore more about managing state transitions in a Redux application.

Video Transcript

00:00So deep freezing objects
00:01and why do we actually do it is really our question.
00:04And why don't we wanna override our state?
00:06And the reason is, is
00:07because when you're inside of your reducer,
00:10that reducer is still in the aggregation element
00:14of the stage creation.
00:16It's not done creating the new stage.
00:19And we don't wanna actually manipulate the new stage yet
00:22because it doesn't even exist yet.
00:24So it would create a lot of bugs if we would,
00:27and we really don't want to create those type of situations.
00:30So beyond that, oso, by applying to the rules,
00:33we're oso getting something really, really cool is
00:36that we could now have a actual clear history
00:38because every single time the state changes,
00:41it means it's a completely new state,
00:42which will make it incredibly easy for you if you choose
00:45to do so, to have some sort of way to go back
00:47and forward in time very, very easily.
00:50But if you manipulate your state directly, you just lose
00:52that feature and, and create all these unexpected behaviors.
00:57So really the deep freeze library is gonna be used here.
01:00It's a NPM package that is inspired by object freeze
01:04to help us make sure that we're not changing things inside
01:07of our state directly.
01:08So let's see how we use it.
01:09Alright, let's continue down the path
01:11of building out our board
01:13and we wanna add to it our first action.
01:16So I'm just gonna go ahead here
01:17and I'm gonna create a new constant.
01:19And let's go ahead and just call this board, uh, board,
01:24board what board change.
01:27All right, so we're gonna change our board.
01:28That's gonna be our action,
01:33and let's go ahead and implement that action.
01:35So in the case that we have here aboard change,
01:39what do we want to do?
01:41Well, let's do it the wrong way first.
01:43And let's assume that I want to, I'm just gonna go
01:45to my state and I'm gonna set my state dot color
01:48one equals action.
01:50Do color one
01:55and let's do it oso color.
02:01There we go. And let's say I do that oso to the second line,
02:04and deliberately we're doing this the wrong way right now.
02:07So first of all, what is my problem?
02:10The problem is, is that I'm modifying the object directly.
02:13And we've talked about this already previously,
02:15how we do not wanna modify objects directly every single
02:19time we're working inside of a reducer.
02:20We wanna basically duplicate a new object
02:23and send that new object with its actual values within.
02:27Now, beyond that, our secondary problem here is that,
02:32well, let's go ahead and see it.
02:33First of all, let's go ahead and create a test for this.
02:36So I'm just literally, I'm gonna go ahead
02:38and I'm gonna set here at it.
02:41And let's say changing color art checking.
02:45Well, let's changing color. There you go.
02:48So changing color, it is going to have a
02:52arrow function, and then we're gonna close it
02:55and put their semicolon.
02:57So let's go ahead and first of all,
02:58Let's go ahead and get our store.
03:00So let's get our current state.
03:01So we're gonna get that from our store,
03:03and we're gonna ask to get our state there.
03:07The next step that we're gonna do is we're gonna go ahead
03:09and actually ask from our store itself to do something.
03:12So we're gonna dispatch a new action
03:14and the type of that action this time
03:15around is gonna be our new action
03:18or our new, uh, directive.
03:20You could say our new constant of board change.
03:23So let's go ahead and add that in there as well.
03:25And when our board is changing, I'm just gonna go ahead here
03:28and set of color one to be, let's say red
03:31and color two, let's say we'll make it yellow.
03:35So we're gonna have a red and yellow board.
03:38Once we've dispatched this event, we want to check
03:41and see that we're actually getting what we expect to get.
03:43So if I go out to my store, get state,
03:48and let's say I check to see if the state itself is
03:54equal to, uh, the value that we wanted to come back.
03:58So I'm gonna create that value here just for the sake of,
04:01so this is my what, what really we're expecting to get.
04:05So I'm going to literally, oh, you know,
04:07let's just put it right inside.
04:08Okay? So it's gonna be a two equal,
04:14and I'm just literally gonna put inside
04:15of here the structure of our object.
04:17And the structure is basically an object which has counters.
04:21And those counters initialize with the 0, 0, 0.
04:25I believe they still do that.
04:27No, they, they initialize empty.
04:29So we initialize our counters empty,
04:32and we also have here a board.
04:36And our board itself, its values should be color red,
04:41color two.
04:43So let me just go ahead and grab those.
04:47And we're expecting that that
04:49to be basically the values of our board.
04:52So again, just walking through this very, very quickly,
04:55we created a new store,
04:57a new action in our store that changes colors.
04:59We've done it incorrectly
05:01by assigning the values directly into our current state.
05:04When we're creating our test itself,
05:06we're literally dispatching that board change
05:09and sending into it the values of color one and color two.
05:13Once we've done that, we want to compare between the object
05:16that we have inside of our store to check to see e expect,
05:22expect that needs to be closed right there.
05:25So once we get that object there,
05:26we wanna check if it equals to this basic object,
05:29our starter object plus this extra information itself.
05:33Now, if we technically run this, it really should work
05:38and it's gonna work, but
05:39it's going, let's see that it works.
05:41You, you're gonna see that we shouldn't have any errors,
05:43but we do have an error where you really wanna be able
05:46to trap and catch a scenario
05:48where we're changing our state directly.
05:50Now, if I technically just went into my store itself
05:54and I went inside and I
05:56Did an object freeze on our full state itself,
06:00then it wouldn't really necessarily always work.
06:02Now the, the reason why it might not always work is
06:06because our object itself is, uh, with deeper layers.
06:10So although our properties of our properties
06:14of counters and board did not change their values,
06:17their explicit values have changed.
06:20So although we could just literally explicitly list out all
06:23them, we could use basically a plugin
06:26that is basically just a recursive run within an object
06:29that makes sure that it's a deep freeze
06:30that not only freezes.
06:32So do that. I'm just gonna go ahead
06:34and I'm gonna ask from NPM install,
06:36and I'm only going to be using this in development,
06:39so I'm gonna use save dev and I'm gonna go ahead
06:42and ask for deep freeze.
06:46And once I have deep freeze installed,
06:48I could literally go into the process itself
06:50of let's go ahead and import it.
06:52So I'm gonna import deep freeze from deep
06:58freeze, and once I have deep freeze,
07:01I could literally just go
07:02and wrap my whole state object,
07:05literally telling you if anything in the state itself
07:07changes directly, then notify me
07:10and, uh, basically break our application,
07:12at least in our testing itself.
07:14And then if we ran it again,
07:15we would get here an error telling us that we cannot assign
07:18that values into the object
07:20because that object has been basically
07:24finalized frozen.
07:25Okay? So that's how we use deep freeze for our process.
07:28Now, in our case, to be able to circumvent this,
07:31what we really wanna do is we wanna create a new object
07:34every time we send out our object.
07:37So literally, I would go ahead and create a new object.
07:40This is kind of a e es six, uh,
07:42an e es six value you could say.
07:47And technically speaking,
07:49what I could literally do is I could first
07:50of all take our current state
07:52and send that current state in if there are multiple
07:55properties, and then literally just change the properties
07:58that are relevant to me.
07:59So maybe board change is a little bit too overboard.
08:02Maybe we wanna do board color.
08:04So board color
08:08and bo board color will change board.
08:11Well, let's do board.
08:15Now, this is important, this explicitness, explicitness,
08:19explicitness is
08:20because our board might have other features, right?
08:23So we wanna change our board color.
08:25In this case, I'm gonna go ahead
08:27and my stated for my actions, I lemme just go ahead
08:29and get out, get them out.
08:31So I have your color one, color two.
08:34Now that we don't have the action directly,
08:36we could type your that
08:38and I could literally just send in our color one, color two.
08:42And what I've literally done here is I took all the
08:45properties inside of our current state,
08:47and it might be other properties as well.
08:50And beyond that, then I appended
08:51to it our color one, color two.
08:53Now, if state itself had a color,
08:54one color tool will just be overridden
08:56because these are coming in afterwards.
09:00So with that said, let me just go ahead
09:01and also make sure that I save this
09:04and make sure that in my test itself, I'm loading up instead
09:07of board change, I'm loading out board color
09:09and I'm dispatching a board color.
09:11And now if I go ahead
09:13and take a peek in my code, we'll see
09:14that now everything is working
09:16and I don't have any errors running.
09:18And that is exactly how we do this.
09:20So let's just go through this quickly.
09:22We did a lot of changes, right?
09:23So we created a new action called border color.
09:27In our border color. What we did is we created a new object
09:31and basically use the three dots, the spread,
09:34the spread attribute of ES six
09:35and objects to basically get all the properties out of
09:38that object into our new object.
09:41Followed by that, we put in all the new properties
09:43or override properties we wanted to use.
09:45In this case, it was the color one
09:47and color two that we fetched out of the action itself.
09:50This, this part itself is the action itself.
09:54And then we triggered a new action by, first of all,
09:56we loaded in our border color
09:58and we dispatched a new action.
09:59And by the way, in the, if you're reading the documentation
10:02as well, or watching videos, other videos,
10:05when when people talk about actions,
10:07they're literally talking about this object itself
10:10that has a type
10:11and has whatever properties that are needed,
10:13if they are needed.
10:15Last but not least, we used Deep Freeze, which was a library
10:18that we've installed with NPM.
10:20And through that library we are running through our object
10:24after we got our state
10:25and freezing all of the attributes,
10:26making sure they cannot be changed, forcing us
10:30to adhere to this structure.
10:32And generally speaking, I encourage
10:33you to do the same thing.
10:35So every time you're, you're building something out,
10:37then you would wanna do this.
10:39And there we go. In this lecture,
10:43we understood why we wanna deep freeze our objects
10:46and why we wanna make sure that we're not
10:48overriding our state.
10:50In the next lecture, we're gonna continue building out our
10:52checker board.

Ready to Level Up Your Skills?

Join thousands of learners on 02GEEK and start your journey to becoming a coding expert today!

Enroll Now for Free!