Simplifying State Management in React Using the Redux Toolkit: Getting Started
Redux is one of the famous state management libraries, which seems to be difficult for beginners to learn because it requires many boilerplates. But we should understand that it’s used by many companies out there and they are still enjoying it, However, it does not mean redux is a great fit for every state management problem.
Problem
let’s take a look at this app, we have three components, the App component, the Login component, and the Profile component.
The Login component handles the login business and logic, and renders the login form(UI), however, we want to access the same user on the profile page.
The first solution could be, passing the props from login to profile, but in react we can not pass props between two siblings components. 😔
The second solution is to write the login business and logic in the App component so that, from the App, we can pass the props to the Profile and the Login if needed. 🤩
Hold on, we can not be happy yet, because we still want to build an app that can handle more than 100 states from different components, in this case, the second solution does not fit, the code will just be unmaintainable.
What if, we have a store somewhere, which will save all our states, and if we need to access or change a state, we just access that store and pick what we want?
Well, here is when redux comes into the place.
Redux installation
Redux is not only for react, it is a JavaScript library for state management, it can also be used with other JavaScript frameworks and libraries, so the library to install first is redux itself.
To connect redux to react, we need another library which is react-redux. Finally, as redux got improved and has now a toolkit that makes it easier to use than it was before, we will need to install the redux toolkit. With the command below we can install everything we need to start working with redux in react.
npm install redux react-redux @reduxjs/toolkit
After having all the libraries installed, let’s start using redux.
Set up redux store.
As we said before, we need a store that will hold all of our application states.
With the redux toolkit, In the index.js
file, we can import the configureStore
function that will mainly contain all reducers in the app.
import { configureStore } from "@reduxjs/toolkit";
For now, as we don’t have any reducers created, we are going to configure the reducers as empty objects. To do so, let's create a variable called store that holds our configure store
function and all the reducers.
...
const store = configureStore({
reducer: {},
});
Set up the Provider.
We have configured our store, now we need to make it accessible everywhere in the app. There comes the react-redux
🤠, It provides a Provider
component that will wrap the App
component to share the store accross children components, It requires a prop which is the store.
In the same file index.js
, let's import the Provider
from react-redux
and pass to it our store previously created.
....
import { configureStore } from "@reduxjs/toolkit";
import { Provider } from "react-redux";
const store = configureStore({
reducer: {},
});
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
Here we are, and the store is ready to be accessible, next we are going to set up the first reducer.
Set up a reducer.
A reducer is just a function that holds the previous state, the action that we want to perform on this state, and returns the current state.
In the /src/components
, let's create a folder called features, which will contain all the reducers.
That folder is called features because it is a standard recommended when using the redux toolkit.
let’s step by step create a user reducer, this reducer will hold the name, age, and email of the user.
- import the
createSlice
function from the redux toolkit, think about a slice of pizza 🍕, it is a small part of the whole pizza, but that holds every single ingredient of the pizza. The same thing, creating a slice will hold the slice name, the initial value, and the reducer itself in one place. isn't that better? 🤗
import { createSlice } from "@reduxjs/toolkit";
export const userSlice = createSlice({
name: "user",
initialState: {
value: {
name: "",
age: 0,
email: "",
},
},
})
- Add the reducer function to the slice, after the initial value, we need to add a function that can change or update the value that we have. That function will be having two parameters, the state which is the previous state, and the action. That is not all, that action is an object with two properties, the payload which is the information that updates or changes the state, and the type, this last property is used if we need to perform different actions according to the type the action, we are not going to focus on it here, just put in mind that it is useful if we don’t use redux toolkit as well.
export const userSlice = createSlice({
name: "user",
initialState: {
value: {
name: "",
age: 0,
email: "",
},
},
reducers: {
login: (state, action) => {
state.value = action.payload;
},
},
});
export default userSlice.reducer;
The above code is the full userSlice
, which holds the reducers object, in our case our reducer is login, every time we will call the login, that login reducer will update the state value with the action payload passed in.
- Back in the
index.js
file, let's now add our reducer in the reducers object, and call it user.
import { configureStore } from "@reduxjs/toolkit";
import { Provider } from "react-redux";
import userReducer from "./components/features/user";
const store = configureStore({
reducer: {
user: userReducer,
},
});
...
Access and modify the state.
We did everything that should be done, from creating a store to setting up a reducer, but the data still needs to be accessed and modified, which is even why we have it.
1. Access a state
To access a state, we use a hook provided by the react-redux library called useSelector
, which is used to grab the state. This hook needs to know which state it should return the value to, for that it requires a callback that will return as the value from our state, it takes an argument state
, this argument can also be named anything.
So to return a value, we need to access the reducer’s value, and we access the reducer value by the name of the reducer then .value.
Note: The reducer’s name is different from the slice name, the reducer’s name, is the name of the property that holds the reducer in the store and it is found in the configureStore function, in our case in the index.js
...
const store = configureStore({
reducer: {
// this is the reducer's name used to access the reducer's value.
user: userReducer,
},
});
...
Let’s create a component called Profile
in /components path, and implement a useSelector hook that returns the value of the user, and then we will render a component that displays the user's value.
import React from "react";
import { useSelector } from "react-redux";
export default function Profile() {
const user = useSelector((state) => state.user.value);
return (
<div>
<h1>Profile page</h1>
<p>Name: {user.name}</p>
<p>email: {user.email}</p>
<p>Age: {user.age}</p>
</div>
);
}
Modify a state
As for accessing the state, we used the useSelector hook; for modifying the state we are going to use another hook provided by react-redux as well called useDispatch.
The use of dispatch requires the actions that will be performed on the state.
Let’s create a simple component called Login, which, once its button is clicked, will update the state of the store.
First, we need to go back into the /components/features/user.js which contains our slice and export the login action.
...
export const { login } = userSlice.actions;
Then, in the components/Login.js** path, let’s implement the dispatch logic.
import React from "react";
import { useDispatch } from "react-redux";
import { login } from "./features/user";
export default function Login() {
const dispatch = useDispatch();
return (
<div>
<button
onClick={() =>
dispatch(
login({ name: "Jonathan", email: "jonathan@gmail.com", age: 20 })
)
}
>
Login
</button>
</div>
);
}
We have first initialized the hook in the components, and then on the click event, we have called the dispatch function with the action to perform.
So what is that object in the login?🤔
Interesting question, remember, to modify the state we need to provide a payload or information or data that will update the state, in this case, our payload is that object with the name, email, and age.
Conclusion
Thank you for reading this article. Now that you know the basics of using Redux and the Redux Toolkit in React, you may explore these capabilities further and won't be disappointed. Contact me at any time if you have any questions.