Learn Redux Toolkit for State Management in React
Resources: https://github.com/harshalranjhani/redux-toolkit-blog-resources
Redux Toolkit is a package that helps you write Redux logic in a more efficient way. It provides a set of tools and best practices that can help you write Redux code faster and with less boilerplate. In this article, we will learn the basics of Redux Toolkit and how to use it for state management in React applications.
What is Redux and why do we need it?
Redux is a state management library for JavaScript applications. It helps you manage the state of your application in a predictable way. Redux is commonly used with React, but it can be used with any JavaScript framework or library.
Redux is useful when you have a complex application with a lot of state that needs to be shared between different components. It provides a centralized store where you can keep all your application state and update it in a predictable way.
One very important use case for Redux is when you have multiple child components that need to share state with each other. When the state is defined in one child component, it can be difficult to pass that state up to the parent component and then down to the other child components. Redux provides a centralized store where all the components can access the state directly.
Without Redux:
With Redux:
What is Redux Toolkit?
Redux Toolkit is a package that provides a set of tools and best practices for writing Redux logic. It is designed to make writing Redux code faster and with less boilerplate. It can help you write Redux code more efficiently and avoid common pitfalls that can lead to bugs and performance issues.
Basic Concepts of Redux Toolkit
Store
The store is the central place where all the application state is stored. It is an object that holds the entire state of the application. You can access the state of the store using the useSelector
hook provided by Redux Toolkit. You can also update the state of the store using the dispatch
function provided by Redux Toolkit.
Reducer
A reducer is a function that takes the current state and an action as arguments and returns the new state. Reducers are pure functions, which means they do not have side effects and always return the same output for the same input. You can define
Slice
A slice is a collection of reducers and actions that are related to a specific part of the state. Slices are used to organize the Redux logic into smaller, more manageable pieces. You can define a slice using the createSlice
function provided by Redux Toolkit.
Action
An action is a plain JavaScript object that describes an event that occurred in the application. Actions are used to update the state of the store.
Selector
A selector is a function that takes the state of the store as an argument and returns a part of the state. Selectors are used to access the state of the store in components.
Dispatch
The dispatch
function is used to dispatch actions to the store. You can use the dispatch
function to update the state of the store by dispatching actions.
Getting started
Let's first create a React App using Vite.
npm create vite@latest
Follow the instructions to create a new React app.
After you've installed the dependencies, we can now install Redux Toolkit.
npm install @reduxjs/toolkit
Also let's install react-redux
which is the official Redux bindings for React.
npm install react-redux
Creating a store
Let's create a store using Redux Toolkit. Create a new folder called store
in the src
directory and create a new file called index.js
inside the store
folder.
import { configureStore } from "@reduxjs/toolkit";
const store = configureStore({
reducer: {
// Add your reducers here
},
});
export default store;
This is the basic setup for creating a store using Redux Toolkit. You can add your reducers to the reducer
object in the configureStore
function.
Let's initially create a counter reducer to store a count variable and then use this reducer in the store.
Create a new file called counterSlice.js
inside the store
folder.
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: {
value: 0,
},
reducers: {
increment: (state, action) => {
state.value += action.payload;
},
decrement: (state, action) => {
state.value -= action.payload;
},
},
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
Here, we have defined a counter slice with an initial state of 0
for value and two reducers increment
and decrement
to increment and decrement the value respectively.
In the reducers
object, we define the reducers for the slice. Each reducer is a function that takes the current state as an argument and returns the new state.
Whatever we pass as the payload to the increment
and decrement
actions will be available as action.payload
in the reducer.
All the payloads passed to the action will be available as action.payload
in the reducer.
Now, let's add this reducer to the store.
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export default store;
Now, we have added the counterReducer
to the store. The counterReducer
will be responsible for updating the state of the counter
slice in the store.
Providing the store to the app
We need to provide the store to the app so that all the components can access the state of the store. This can be done by wrapping the app component with the Provider
component provided by react-redux
.
Open the main.jsx
file in the src
directory and wrap the App
component with the Provider
component.
// ...
import { Provider } from "react-redux";
import store from "./store/index.js";
// ...
ReactDOM.createRoot(document.getElementById("root")).render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>
);
Now, the store is provided to the app and all the components can access the state of the store using the useSelector
hook provided by Redux Toolkit.
Using the store in components
Let's create two child components of the App
component, one to increment/decrement the counter value and another to display the counter value.
Inside the App
component itself, create a new component called CounterDisplay
to display the counter value.
const CounterDisplay = () => {
const count = useSelector((state) => state.counter.value);
return (
<div>
<h2>
Counter Display{" "}
<span className="component-type">(I am the child 1)</span>{" "}
</h2>
<p>
<b>Counter value: {count}</b>
</p>
</div>
);
};
And another component called CounterControls
to increment/decrement the counter value.
const CounterControls = () => {
const dispatch = useDispatch();
return (
<div>
<h2>
Counter Controls
<span className="component-type">(I am the child 2)</span>
</h2>
<button onClick={() => dispatch(increment(1))}>Increment</button>
<button onClick={() => dispatch(decrement(1))}>Decrement</button>
</div>
);
};
Here, we are using the useSelector
hook to access the state of the store and the useDispatch
hook to dispatch actions to the store.
We are also passing the payload 1
to the increment
and decrement
actions. This payload will be available as action.payload
in the reducer.
The imports for useSelector
and useDispatch
are as follows:
import { useSelector, useDispatch } from "react-redux";
Now, let's use these components in the App
component.
import "./App.css";
import { increment, decrement } from "./store/counterSlice";
import { useSelector, useDispatch } from "react-redux";
function App() {
// Child component: Counter rDisplay
const CounterDisplay = () => {
const count = useSelector((state) => state.counter.value);
return (
<div>
<h2>
Counter Display{" "}
<span className="component-type">(I am the child 1)</span>{" "}
</h2>
<p>
<b>Counter value: {count}</b>
</p>
</div>
);
};
// Child component: CounterControls
const CounterControls = () => {
const dispatch = useDispatch();
return (
<div>
<h2>
Counter Controls
<span className="component-type">(I am the child 2)</span>
</h2>
<button onClick={() => dispatch(increment(1))}>Increment</button>
<button onClick={() => dispatch(decrement(1))}>Decrement</button>
</div>
);
};
return (
<>
<h2>
Redux Toolkit Implementation{" "}
<span className="component-type">(I am the parent component)</span>{" "}
</h2>
<CounterDisplay />
<CounterControls />
</>
);
}
export default App;
Now, you can see the counter value displayed on the screen and you can increment/decrement the counter value by clicking the buttons.
This clearly means that the two child components are sharing the state of the counter value using the Redux store. There is no need of passing the state up to the parent component and then down to the other child components. This is the power of Redux Toolkit.
Conclusion
In this article, we learned the basics of Redux Toolkit and how to use it for state management in React applications. We created a store using Redux Toolkit, added a reducer to the store, provided the store to the app, and used the store in components to share the state between them. We also learned how to use the useSelector
and useDispatch
hooks provided by Redux Toolkit to access the state of the store and dispatch actions to the store. I hope this article helped you understand the basics of Redux Toolkit and how to use it in your React applications.
Related articles
Development using CodeParrot AI