In my last article, I discussed state management and its importance in a react application.
In this article, we would be diving deep into Understanding state management with Redux and Redux toolkit.
What is Redux?
Redux is a popular open-source library for managing state in JavaScript applications, particularly those built with React. It provides a predictable state container that makes it easier to manage and update the application state in a consistent way.
With Redux, the state of your application is kept in a store, and each component can access any state that it needs from this store.
When to use Redux?
Redux is a powerful tool for managing state in JavaScript applications, but it's not always necessary to use it. Here are a few scenarios where Redux can be particularly useful:
Large applications with a complex state: If your application has a lot of different components and features that all need to access and modify state, it can become difficult to manage all of that state without a central system like Redux.
Shared state between components: If you have multiple components that need to share the same state, Redux can help ensure that the state is consistent across all of those components.
Predictable state management: Redux enforces a strict set of rules for how the state can be modified, which can help prevent bugs and make it easier to understand how the state changes over time.
However, if your application is relatively simple, or you don't anticipate needing to manage a lot of shared states, Redux might not be necessary. In general, it's a good idea to consider the complexity and scale of your application before deciding whether to use Redux or not.
I had a bad experience with redux the first time I used the package, simply because it was way too complicated for me to grasp the whole concept. At the time redux was at its infant stage, there were too many moving parts and High order Components, which made me dump learning redux for a while, but the need for a well-structured state management tool kept coming up, so I had to go back and learn redux after months of running from it, but at this time a new tool had emerged, a tool that makes the whole process of managing state with redux a lot simpler and robust, this tool is called Redux Toolkit.
The Redux Toolkit package is intended to be the standard way to write Redux logic. It was originally created to help address three common concerns about Redux:
"Configuring a Redux store is too complicated"
"I have to add a lot of packages to get Redux to do anything useful"
"Redux requires too much boilerplate code"
Whether you're a brand new Redux user setting up your first project, or an experienced user who wants to simplify an existing application, Redux Toolkit can help you make your Redux code better. The best route to understanding redux is by learning the Redux toolkit.
Disclaimer: Redux is a stand-alone library that can handle and manage state, Redux Toolkit was built on top of Redux.
Now let's get our hands dirty with some code. We would be going through the workflow of managing state with Redux and Redux Toolkit
First, we start by installing redux and redux
Installation
Using create-react-app
The recommended way to start new apps with React and Redux is by using the official Redux+JS template or Redux+TS template for Create React App, which takes advantage of Redux Toolkit and React Redux's integration with React components.
# Redux + Plain JS template
npx create-react-app my-app --template redux
# Redux + TypeScript template
npx create-react-app my-app --template redux-typescript
An Existing App
npm install @reduxjs/toolkit
npm install react-redux
In this article, we would be setting up redux toolkit from an existing create-react app.
After installing the create-react app, the first step in configuring your application with redux is to create a store.
Create a Redux Store
A Store in Redux is basically the powerhouse of the entire application, it is a file that configures and manages the state of your application. The store holds the application state and provides several methods for interacting with that state.
Create a file named src/app/store.js
. Import the configureStore
API from Redux Toolkit. We'll start by creating an empty Redux store, and exporting it:
import { configureStore } from '@reduxjs/toolkit'
export const store = configureStore({
reducer: {},
})
This creates a Redux store, and also automatically configures the Redux DevTools extension so that you can inspect the store while developing.
Expose the Redux store to React
Once the store is created, we can make it available to our React components by putting a React-Redux <Provider>
around our application in src/index.js
. Import the Redux store we just created, put a <Provider>
around your <App>
, and pass the store as a prop:
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { store } from './app/store'
import { Provider } from 'react-redux'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
Create a Redux State Slice
Add a new file named src/features/counter/counterSlice.js
. In that file, import the createSlice
API from Redux Toolkit.
Creating a slice requires a string name to identify the slice, an initial state value, and one or more reducer functions to define how the state can be updated. Once a slice is created, we can export the generated Redux action creators and the reducer function for the whole slice.
Redux requires that we write all state updates immutably, by making copies of data and updating the copies. However, Redux Toolkit's createSlice
and createReducer
APIs use Immer inside to allow us to write "mutating" update logic that becomes correct immutable updates.
import { createSlice } from '@reduxjs/toolkit'
const initialState = {
value: 0,
}
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
// Redux Toolkit allows us to write "mutating" logic in reducers. It
// doesn't actually mutate the state because it uses the Immer library,
// which detects changes to a "draft state" and produces a brand new
// immutable state based off those changes
state.value += 1
},
decrement: (state) => {
state.value -= 1
},
incrementByAmount: (state, action) => {
state.value += action.payload
},
},
})
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions
export default counterSlice.reducer
You can add as many reducers into a slice, but ensure to export them because this would expose the reducers to the react application.
Add Slice Reducers to the Store
Next, we need to import the reducer function from the counter slice and add it to our store. By defining a field inside the reducer
parameter, we tell the store to use this slice reducer function to handle all updates to that state.
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})
Use Redux State and Actions in React Components
Now we can use the React-Redux hooks to let React components interact with the Redux store. We can read data from the store with useSelector
, and dispatch actions using useDispatch
. Create a src/features/counter/Counter.js
file with a <Counter>
component inside, then import that component into App.js
and render it inside of <App>
.
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'
export function Counter() {
const count = useSelector((state) => state.counter.value)
const dispatch = useDispatch()
return (
<div>
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
)
}
Now, any time you click the "Increment" and "Decrement" buttons:
The corresponding Redux action will be dispatched to the store
The counter slice reducer will see the actions and update its state
The
<Counter>
component will see the new state value from the store and re-render itself with the new data
What's Next?
I recommend going through the official Redux Toolkit Documentation, which will give you a complete understanding of how Redux works, what Redux Toolkit does, and how to use it correctly.