DEV Community

DNelson35
DNelson35

Posted on

Redux and Redux Toolkit for Beginners: A Comprehensive Tutorial

Introduction

Redux is a Useful and Powerful state management library used in many large Javascript Projects. If you are familiar with React useContext Api then you should have an idea of why and when you might want to use a state manager. But if not don't worry I got you. In today's blog I will be going over what Redux is, How it works, Why we might want to use Redux,And how to use it.

What is Redux

As mentioned before, Redux is a state management library. Essentially it is used for managing State that you may need all throughout your project and keeping it in one place so you can grab and use it where you need it. You may be thinking “This sounds a lot like useContext” and you're not wrong they do relatively similar things but when it comes to scale and how they work they are very different. One major difference to note is Redux is its own stand alone library designed specifically to optimize state management and updating state. With that in mind Redux can be a more performant option as your project grows. On the other hand Redux takes several steps to set up and can be unnecessary for some smaller applications. With smaller applications that don't really need to take advantage of Redux state optimization I would suggest going for the much more lightweight and easy to use useContext api provided by React.

If you do choose to use Redux there are three things redux needs to work properly, one you will need to make a store, two you will have to create reducers and three actions. Don’t worry we will be discussing all of this in this blog.

How Redux Works

When we talk about how redux works it really comes down to how it handles state. State in Redux is managed in an immutable object, and every update to the Redux state results in a copy of sections of the state, plus the new change. Redux uses events called "actions" to manage and update the state. Actions are dispatched to the store, which then updates the state based on the rules defined in reducers. The updated state can then be accessed by any component in the application, making Redux a powerful tool for managing global state in complex applications. In terms of memory management, Redux uses a technique called "structural sharing" to make these state updates efficient. Instead of creating a deep copy of the entire state (which can be expensive in terms of performance), Redux creates a shallow copy of the parts of the state that are being updated. It then replaces the old parts of the state with the new ones. This way, Redux doesn't need to create a deep copy of the state every time an update is made, which can significantly improve performance for large state objects.

As for the garbage collector, it will clean up the old state objects once they are no longer referenced by the Redux store. This is because Redux uses immutable data structures, and once a new state is created, the old state is no longer needed and can be safely discarded

How Redux three Steps Work

First we have, reducers. They specify how the application's state changes in response to actions sent to the store. Actions only describe what happened, but don't describe how the application's state changes. That's the job of a reducer. A reducer is a pure function that takes the previous state and an action, and returns the next state.

Then we have actions. when an action is dispatched, the store runs the reducer function with the current state and the dispatched action. The reducer determines how the state should change in response to the action, and returns the new state. The store then updates its state with the new state returned by the reducer. It does this by creating a shallow copy of the old state comparing them and updating the part that has changed.

Finally we have the Redux store. It notifies all parts of the UI that are subscribed that the store has been updated. Each UI component that needs data from the store checks to see if the parts of the state they need have changed. Each component that sees its data has changed and forces a re-render with the new data, so it can update what's shown on the screen.

Now that we have a firm grasp of how redux works and how it is optimized for handling state, Lets move on into how to set up Redux and Redux Toolkit in and application.

Set Up Redux

Create a App

The first thing we are going to want to do is to create a to do this we run this code in our terminal:

npx create-react-app project-name

cd project-name
Enter fullscreen mode Exit fullscreen mode

Install Libraries

After we have created our application directory and navigated into it we are going to run these commands in the terminal

npm install react-redux
npm install @reduxjs/toolkit
Enter fullscreen mode Exit fullscreen mode

Reducers

Now that we have the libraries installed we can go ahead and start setting up redux by first setting up a reducer then we will move on to setting up the store. It is important to note that the these slices are how we create state for redux. in the code below we created a state called posts and if we wanted to create more state we have two options we can create another slice for another unique state. set our initial state to an object with keys and update the values assigned to the keys with reducers. here is an example of a reducer:

import { createSlice } from '@reduxjs/toolkit'

const initialState = []

Const postSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {
    addPost: (state, action) => {
     state.push(action.payload)
    }
  }
})

// or we could set our initial state to an object like this
// const initailState = {
 // post: [],
 // anotherstate: '', 
//}
// in the reducers we would update this by saying //state.post.push(action.payload) instead

export const {addPost} = postsSlice.actions
export default postsSlice.reducer   
Enter fullscreen mode Exit fullscreen mode

ok so let me explain what is happening here.

First, we're importing the createSlice method from the @reduxjs/toolkit library. This method is used to generate action creators and action types that correspond to the reducers and state. This is for reducing the boilerplate required to add data to Redux.

Next, we're defining the initial state of our application. This is done so our reducer knows what we want our state to be before any updates are made. In this case, we're setting the default state of our reducer to be an empty array.

Then, we're creating a slice of the Redux store using the createSlice method. This slice is given the name 'posts', and it is initialized with the initialState object. The createSlice method takes an object as an argument, which includes the name of the slice, the initial state, and the reducers that the slice will handle.

Inside the createSlice method, we're defining a reducer function named addPost. This function takes the current state and an action as arguments. The action's payload is pushed to the posts array in the state. This reducer function will be used to handle the 'addPost' action.

The createSlice method returns an object that contains the reducer function and the action creators generated. In this case, the addPost action creator is generated, which can be used to dispatch the 'addPost' action.

Finally, we're assigning the object returned by the createSlice method to the postSlice constant. This object contains the reducer function and the action creators, and it's used to interact with the 'posts' slice of the Redux store. then we export the actions to use in our application as well as the reducer created by createSlice to use in the redux store.

Store

Next lets take a look at the store and see how we set this up and what it does. Here is an example of how our Store would look with the reducer from above

import { configureStore } from '@reduxjs/toolkit'
import postsReducer from '../reducers/postsSlice'
//note that my reducer here is imported from a folder called reducer. depending on how you set up your file structure this could be different for you.

const store = configureStore({
  reducer: {
    posts: postsReducer,
  },
})

export default store
Enter fullscreen mode Exit fullscreen mode

First we have the configureStore method imported from the @reduxjs/toolkit library. This method is used to create a Redux store. The store is What we will use in our top level component to pass into the provider component provided by react-redux library this makes the store available to our entire application.

Then postsReducer is imported from a file named postsSlice located in a reducers folder. The exact location of this file may vary depending on your project's file structure.

We use the configureStore method to create a Redux store. This store is given the name store. The configureStore method takes an object as an argument, which includes the reducers that the store will handle. In this case, the postsReducer is assigned to the user key in the reducer object.

The store is then exported as the default export of the module. This allows it to be imported and used in other parts of the application.

Last Step The Provider

The last thing we have to do to finish setting up redux in our application is to wrap our application in the provider form the react-redux library and pass our store as a prop here is an example:

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './pages/App'
import {BrowserRouter as Router} from 'react-router-dom'
import { Provider } from 'react-redux'
import store from './store/store'

const root = ReactDOM.createRoot(document.getElementById('root'))

root.render(
  <Provider store={store}>
    <Router>
      <App />
    </Router>
  </Provider>
)
Enter fullscreen mode Exit fullscreen mode

Here you can see we import our store and the Provider. Then we Wrap the entire application in the provider and pass the store as a prop. This gives the entire application access to our store so we can update state. Thats is we are done setting up redux the next step is how do we use it from here

How to use Redux

Now that our Redux store is set up. We have our slice for creating the reducer and the actions, And we have set up our provider we need two more things to be able to use the store.

We need useDispatch, and useSelector. these methodos are what let us grab state form the store and dispatch actions to update the state in the store

if we want to grab our post from redux we could do somthing like this:

import React, {useState} from 'react'
import { useSelector } from 'react-redux/es/hooks/useSelector'

function Profile() {
  const posts = useSelector(state => state.posts)
  console.log(post.length) ==> 0
// our initial state is an empty array
  return <h1>hello world</h1>
}
Enter fullscreen mode Exit fullscreen mode

Now lets update the state so we no longer have an empty array

import React, {useState} from 'react'
import { useSelector, useDispatch } from 'react-redux/es/hooks/useSelector'
import { addPost } from '../reducers/postSlice'

function Profile() {
  const posts = useSelector(state => state.posts)
  const dispatch = useDispatch()
  const post = 'hello'

  const handleClick = () => {
    dispatch(addPost(post))
  }
// now if we click the button we can see the state update to // show the added posts. 
  console.log(posts) ==> ['hello']
  return <button onClick={handleClick}>Click</button>
}
Enter fullscreen mode Exit fullscreen mode

conclusion

We are finished by this point you should have a working application using Redux and Redux Toolkit. I show how to use Redux Toolkit Query in this blog if you would like to know more about that leave a comment. Thank you for following along. If you have any questions please leave me a comment and I will respond as soon as I can.

Top comments (0)