Redoing Redux — A deep dive into Redux and Redux Toolkit

Leon
17 min readAug 1, 2022

The year is 2021, the day August 18th.

I’m 78 days into my #100DaysOfCode journey and I’m understanding Javascript a lot more. At this point in my journey I’ve dabbled in React, Node and Express, all javascript libraries/frameworks. At this point in the journey, I’m learning how to build a React app and manage the state using Redux. I have no idea what I’m doing with Redux.

Day 78 from me documenting #100DaysOfCode on Twitter

Fast forward to 2022, almost a full year later. I understand Javascript and React a lot more. I don’t know what I’m doing a little less now. I’m ready to take Redux on again.

What is Redux

Redux manages state similar to context api. With functions similar to the useReducer hook.

It’s similar to ContextApi in the sense that you can create and assign app wide state that you can provide to any component that needs it. It also uses the “Provider” component as a wrapper to the components that you’d like to provide the state to.

Similar to useReducer in the sense that, in order to manipulate the state you must have actions, action types and dispatch those actions. The terminology used is similar. So if you understand useReducer, Redux shouldn’t be too hard to understand. If you don’t , I have a blog post on how to use useReducer here.

Getting Started With Redux

In order to bring redux into a React project you must:

  • run the command:
npm install redux react-redux

After our redux package installs, let’s focus on our file structure.

We’ll start by creating a store folder in our React src folder

folder structure

Next inside our store folder we’ll create an index.js file. This file can be named whatever you’d like but we’ll stick to convention and name it index.

Inside of our index.js file. We

import { createStore } from ‘redux’

This function is used to create our store where we will store our state. The createStore function then looks for a reducer function that it references in order to manipulate our stored state values.

To showcase Redux we’re going to work on creating a counter application.

So we create a reducer function. Like the useReducer hook our redux reducer function takes state and action as arguments. We then return the state and any action type we desire.

In this case we have a counterReducer that starts with an initial value of 0 with two action types ‘increment’ and ‘decrement’ both which respectively return the state +/- 1 of the previous state.

Now that we have our store and index file set up. We want to provide our app with this global state information.

IMPORTANT: Redux can be used on any component and their children. You do not have to use it globally app wide, but in this example we will be using it app wide.

In order for us to provide our application with the global state value, we go down to our application’s root index.js file. Import Provider from ‘react-redux’

and wrap our App component with that Provider.

Next we want to import our store and pass it in as a prop to our Provider

  • Now our app component has access to our Redux store and the state values.

We want to use our Counter component inside our App.js file with Redux

Inside our Counter component we

import { useSelector } from ‘react-redux’

The useSelector hook is a custom hook created by the React Redux team that allows us to select a specific part of our state managed by our store.

Next we want to call useSelector in our Counter component. We pass in a function. That function takes the state and then points to the specific property we’d like to access on that state.

assigning useSelector a value

If you remember we have a counter property on our state form our store.

We’re going to use the useSelector hook in order to grab that counter value.

We now render out our counter in our JSX and voila.

Counter Component

Dispatching Action’s with React-Redux

In order to dispatch actions inside a React component using Redux, we must first import the useDispatch hook provided to us in the react-redux package.

destructured import of useSelector and useDispatch

We then declare a dispatch variable and define it with our useDispatch hook

We then want to create increment and decrement handler functions. In which we we call our dispatch function and give it a type (again similar to useReducer).

IMPORTANT: This type NEEDS to be named the same way it is in our store index.js file.

If you remember we set our reducer up to look for action types. Like so

So we have to set up our increment and decrement handlers to point to those same action types.

note : the top function should be ‘incrementHandler’ not imcrementHandler ← pardon the typo

We then assign our buttons to their respective handlers using the onClick prop

And now in our app, the Increment and Decrement buttons update our store and increase and decrease our counter.

Payloads in Redux

So far we’ve only worked with simple actions. Actions with just a simple type that increment and decrement our counter. While this works for this project. We inevitably will come across scenario’s where we need to utilize Redux for more than this. For example. Let’s say we wanted to add a button that increases our counter by 5. While we can just hard code this in our store’s counterReducer function and set the action.type property to ‘increase’ and have it return our counter increased by 5. There is a more syntactically appropriate, mutable and more dynamic way of doing this. We do so by passing another property into our dispatch function. This second argument can be named whatever you like, but usually it’s payload. In this case we’ll use “amount”

Let’s start first by updating our reducer in our store. We want to create an increase action type where we add 5 to our counter when a button is pressed. We do so by inserting this piece of code into our reducer.

Next let’s add an increase function to our Counter component.

As I mentioned, there are some differences in both our condition in our reducer function as well as another argument in our dispatch function in our increaseHandler. In our conditional we tell our code to return the current state of the counter. And in addition to our action type, we’re looking for an action amount as well, we then want to add our current value from our counter to that amount. Now back to our dispatch function. Like in the useReducer hook, we can add a value to our action. In this case, we named our value “amount” we then assigned 5 to this amount. So when our increase type Is triggered. We will get our current state plus 5.

buttons in our Counter component
updated counter render

Working with multiple states in Redux

Up until now we’ve managed one state in our counter app. While this is useful and we’ve showcased how useful Redux can be in doing this, we have yet to tap into the true potential of Redux. Again, like useReducer ( I know, I’m beating a dead horse here) Redux can manage multiple States. Let’s create an action to toggle our counter.

We start by changing our reducer function and making it less bloated. Currently we have it written inline with our counter default value passed into the function as an argument.

current bloated reducer function.

Nothing wrong with this, but if we plan on adding more properties to our state we should extract this default value. We’re going to do that by declaring an initialState value

initialState declared with updated reducer function.

Now we have two values in our state. Counter and showCounter.

Now we have a way to toggle our counter. By setting the state to a boolean. Let’s continue down into our reducer function.

Note that when you add a new key value pair to your state, you must update it in every conditional inside the reducer. For instance. Now that we have our showCounter state, we have to add it to our increment, decrement and increase conditions. Granted, we won’t be updating it. So we’ll return what the state is by setting the value to state.showCounter but it needs to be in the return statement nonetheless.

example of returning multiple states

But now we have to create a toggle condition. We’ll do this by setting the action type to ‘toggle’ and returning the showCounter key as the opposite of what it’s already set as.

toggle condition added to our reducer

Now we have to go back to our Counter component and create a toggleHandler function. We already have a Toggle Counter button, but we have to set the dispatch action in a toggle handler function.

In our toggleHandler function we set the dispatch type to toggle, as we did many times before above. What’s different now in this scenario is we must call the useSelector hook again and this time target the showCounter state in our store.

By calling useSelector hook again, we point to where in our state we want to target. We then render our component conditionally by using the show variable.

Working with Slices in Redux Toolkit

Up until this point, I’ve given you all the information you need to manage state by using Redux out of the box. I’ve shown you how to:

  • Set up a store.
  • Set up a reducer function.
  • Use the Provider component as a wrapper for the components you’d like to apply global state.
  • Use the useSelector hook to point to the particular part of the state you’d like to update.
  • Use the useDispatch hook to dispatch actions to tell your reducer function which action to take.
  • Work with payloads within Redux.
  • Manage multiple states.

If you’ve followed this guide correctly and I’ve done a good enough job of explaining how everything works, you should be able to start using Redux to manage state in your next project.

But what if I told you there was an even easier way to do everything I’ve already explained. Go through each bullet point in the beginning of this section and….think bigger (in my best Steve Jobs impersonation).

Think Redux but with the batteries included…Well look no further, well read further if you’d like to know what I’m getting at but metaphorically look no further. I introduce to you, better Redux. But you can call it Redux Toolkit.

Before we dive into Redux Toolkit let’s install it

We do so by running the command:

npm install @reduxjs/toolkit

Creating Our First Slice

Toolkit allows us to manage our state in small pieces as opposed to one bloated file. These pieces are referred to as slices. Let’s create our first slice in our index file inside our store.

Import createSlice from @reduxjs/toolkit by destructuring it.

import { createSlice } from '@reduxjs/toolkit'

Now that we’ve imported our slice we get started with using it by calling the createSlice function and it takes an object as an argument. createSlice is used to create a slice or a piece of our global state. This helps to make the code maintainable. Every slice needs a name. So we use name as a key in the object inside our function call, we’ll name this first slice ‘counter’ and use it to manage our counter state. We also need to add the initial state as a key value pair inside our slice. Lastly our conditional statements from our reducer function, we pass in as an object into our slice with the key ‘reducers’. After all those steps are taken our slice should look like so.

In order to use our slice we want to first declare it as a variable. A slice removes the need to use multiple conditions and returns inside a reducer. Instead we just create our reducers object like above and we pass state as an argument in all the methods we use and write our code within the objects. So we pass state into all our methods under the reducers key, add our add our action or what we’d like to happen when that method is triggered inside the object and our slice should now look like this.

Now that we’re using Redux toolkit we need to use a different function as our store. We want to call configureStore from redux toolkit. So our import should now look like this…

import { createSlice, configureStore } from '@reduxjs/toolkit'

So we now replace our current store variable which we defined with createStore with configureStore. But unlike createStore where we passed our reducer function as an argument. With configureStore we aren’t passing our createSlice function. Instead we’re passing it an object with the key reducer.

Note we pass both createStore snd configureStore a reducer (singular) key and not reducers even though our slice has the key property of reducers. This is because redux is looking for one single global reducer.

With this in mind. As our reducer key value, we can pass configureStore our createSlice.reducer property.

Dispatching Actions With Slices

Now with our slice and store set up the question of how do we dispatch actions arises. When creating a slice, the object has a function/method on our slice called actions.These methods are called action creators. So we can access these by calling actions on our slice. These action creators already have unique type auto generated. With this we no longer have to create switch statements, come up with unique names for our actions or worry about avoiding typos. Let’s store our counterSlice actions to a variable and export it.

Now let’s take a look at how to dispatch these actions in our Counter component.

We start by importing our counterActions variable in our Counter component.

Now with our counterActions object imported. We can call our reducer methods inside our handlers like so.

For our increaseHandler , the function that we had a payload in and we increase our counter by a specific amount besides 1, we call counterActions.increase on our dispatch function like we did above. Except this time, we pass the amount we want to increase our counter by as an argument.

IMPORTANT: Unlike the previous step we took to add a payload to our dispatch function where we could just name our second parameter anything we want, Redux Toolkit is specifically looking for the word payload in our slice.

So back in our counterSlice object in our index.js in our store file. We must update our increase reducer from…

To

Managing Multiple Slices

As of now we’ve only worked with our counter. To really showcase the power of Redux Toolkit and slices, we’re going to work with some dummy authentication. So we’re going to create a new slice for authentication state.

We’ll start by first creating an initialState variable for our authentication. We’ll call this initialAuthState.

We want to do this because we are now going to be using multiple states and we want to keep convention by using the word initial.

We then want to create an initialAuthState variable to which we define as an object with isAuthenticated set to false as our default state.

Now we want to create our authSlice. We do so the same way we created our counterSlice. By calling createSlice which takes an object as an argument. With a name key, our initialAuthState and our reducers object with our actions/methods.

We now add this new slice to our store.

IMPORTANT: WE DO NOT CREATE A NEW STORE. We only have one store for Redux.

Instead. We now destructure our reducer key and make the value an object where we point to both our counter and our authentication slice. Like so

Now that we have our authSlice in our store, we can export our actions like we did with our counter.

Now we get into the fun part.

We’re going to set our application up to show a login and logout screen by using and setting the state.

When the user is not authenticated we will provide them a login form.

Once they are authenticated we will show them their User Profile and our counter.

Now that we have both our slices lets think about how we’re going to achieve this authentication mapping.

First we want to make it so the login form shows when the user is not authenticated. As you remember, the initial state for our isAuthenticated property in our auth slice is false which means our user will not be authenticated to begin with. So let’s start by going to our App Component We’re going to do the same process as our Counter component. We want useSelector from react-redux, we want to point to our auth state

In our App component we want to render our login form which we have in our Auth component if the user is not authenticated.

If the user is Authenticated we want to render our UserProfile component and our Counter component.

Wouldn’t it be nice if this is all we had to do? Unfortunately this is still only the beginning. As of right now we’d only get the login form and nothing would happen if we entered credentials and pressed the login button. This is because we must dispatch actions to mutate our authentication state. The good news is, we’re done working with our App component.

Now we move on to our Auth component.

In this component we want to import the useDispatch function from react-redux and our authActions from our store.

Once we have our imports. We want to declare our dispatch variable as the useDispatch function, like in our Counter component.

Now that we have our dispatch function let’s think about our necessary steps.

As of right now our Auth component looks like this:

It does nothing when our Login button is clicked. In order to make it so that our authentication state is controlled by our login button we first must create a function that dispatches our login action.

If you recall in our authSlice we had two actions, login and logout.

If you look closely you see that our login action set’s isAuthenticated to true. That is what we want. Since we already know how to dispatch actions, lets do that now by creating a loginHandler function.

We want this function to be called on form submission. When a form is submitted it automatically sends a request and refreshes the page. To prevent this we call preventDefault() which is a built in javascript function to prevent refreshes on form submissions.

Moving on. We dispatch our login action on form submission and now that we have this function we want to bind it to form submission by pointing to it onSubmit.

Now we’re two thirds of the way done. We now created a way to change our users Authentication state by clicking the login button.

We want the user to now be able to logout and bring them back to the login form. We’re done with our Auth component.

When our user is Authenticated our Header component renders this navigation bar.

We want to bind our Logout button to our logout action from our authSlice.

To do so we move on to our Header component. We import useSelector as well as useDispatch from react-redux.

Like before we want to declare our useDispatch function as dispatch. And like in our App component we want to use our useSelector function to point to our authentication.

So now our Header component currently looks like this:

Like in our Auth component we need to create a logout function and bind it to our logout button. But we also need to render our nav buttons based on whether or not our user is Authenticated. We don’t want to show the nav button’s if the user isn’t Authenticated.

Let’s start by making our logoutHandler function.

Like our loginHandler we want to call dispatch and pass in our authActions with our logout action.

To bind this function to our logout button we point to us on the onClick prop on our button element.

Now let’s move on to our conditional rendering of our nav buttons. As I mentioned before we want our nav buttons only to appear if our user is authenticated. We’ve already declared our isAuth variable and now we want to render those buttons conditionally.

Now we’re done in our Header component. And all of our components.

After all that is done what we should get is this…

And that’s all folks. If you’ve made it all the way to the end of this article, I’d like to personally thank you for reading. I haven’t been in the tech field that long so my verbiage and technical writing may not be what you expect. But I see that as an advantage as much as it may be an obstacle. It’s advantageous because I’m able to explain these concepts in my own words, in plain english without getting too techy so the average reader can understand this without need much of a tech background. But with all that being said. From the bottom of my heart, thanks for reading. If you’d like to learn more about Redux, you can do so here. And if you’d like to learn more about Redux Toolkit you can do so here. Thank you again for your time, until next time. Happy building!

--

--