Understanding Redux (Part 2): Creating a small Redux powered React App in 10 easy steps (with code snippets)
Soham De Roy
Posted on May 26, 2022
Before proceeding with this blog, I would recommend first going through Part 1 of the Understanding Redux series which can be found by clicking on this link Understanding Redux (Part 1): Demystifying Store, Action and Reducers. That will help you understand the current article. In the Part 1 blog, I have tried to explain the fundamental principles/ concepts of Redux. I have covered what is Store, Actions, and Reducers, what makes Redux predictable along with an example.
In this current article, we will try to set up our own redux powered react application. We will go through how to create Store and provide it to the Application, write Actions, Dispatch them on user interactions, make Reducers and update the store, read the store from other components which are children of App and many more. I'll provide all the important code snippets along the way so that you can quickly spin up the application.
To give a glimpse in the beginning itself, this is what we will finally build
We will be creating a basic application where we can add and remove item in the cart. We will manage the state changes in the redux store and display the information in the UI.
Code Setup
1. Create a react app with the create-react-app command
npx create-react-app react-app-with-redux
2. Go to the newly created folder using
cd react-app-with-redux
3. Install redux and react-redux library using the commands
npm install redux react-redux
4. Run the application using
npm start
5. Creating Reducer
First create a folder inside src
named actionTypes
and create a file inside it named actionTypes.js
. This file will contain all the actions the application will be dealing with. Add the following lines in actionTypes.js
export const ADD_ITEM = "ADD_ITEM";
export const DELETE_ITEM = "DELETE_ITEM";
As we are making an app where we will have the functionality of adding and deleting an Item, hence the above two action types.
Next create a folder inside the src
called reducers
and create a new file in it named cartReducer.js
. This file will contain all the reducer logic related to the cart component. (Note: We will create the view/ UI in the step 8). Add the following lines in the cartReducer.js
.
import { ADD_ITEM, DELETE_ITEM } from "../actionTypes/actionTypes";
const initialState = {
numOfItems: 0,
};
export default const cartReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_ITEM:
return {
...state,
numOfItems: state.numOfItems + 1,
};
case DELETE_ITEM:
return {
...state,
numOfItems: state.numOfItems - 1,
};
default:
return state;
}
};
As we discussed it Part 1 of this blog, we created an initial state for the app and assigned it to the default parameter of state
in the cartReducer
function. This function switches on the type of action dispatched, and which ever case matches with the action type, makes necessary changes in the state and returns a fresh new instance of the updated state. If none of the action type matches, then the state is returned as it is. Finally we make a default export of the cakeReducer
function to use it in the store creation process.
6. Creating the store and providing it to the App
Create a file inside src
with the name store.js
and create the store using the command
const store = createStore()
Add the following lines in store.js
import { createStore } from "redux";
import { cartReducer } from "./reducers/cartReducer";
const store = createStore(cartReducer);
export default store;
Now it's time to provide this store
to the App
component. For this we make use of the <Provider>
tag that we get from the react-redux
library. We wrap the whole App
component inside the <Provider>
tag using the following syntax.
// rest of the code ...
<Provider store={store}>
<div>App Component</div>
// child components of App/ other logic
</Provider>
// rest of the code ...
By wrapping the App
component inside the <Provider>
tag, all the children component of App
will get access of the store
. Please visit the Part 1 of the blog series to know more.
Continuing with the App.js
, add the following lines in the file.
import "./App.css";
import { Provider } from "react-redux";
import store from "./store";
function App() {
return (
<Provider store={store}>
<div>App Component</div>
</Provider>
);
}
export default App;
7. Create Actions
Now create a folder inside src
called actions
and create a file inside it called cartAction.js
. Here we will add all the actions to be dispatched on some user interactions. Add the following lines in the cartAction.js
import { ADD_ITEM, DELETE_ITEM } from "../actionTypes/actionTypes";
const addItem = () => {
return {
type: ADD_ITEM,
};
};
const deleteItem = () => {
return {
type: DELETE_ITEM,
};
};
export { addItem, deleteItem };
In the above code we created two action creators (pure JS functions that returns action
object) called addItem()
and deleteItem()
. Both the action creators returns action
object with a specific type
. Note: Each action
object must necessarily have a unique type
value. Along with it any additional data passed with the action object is optional and will depend on the logic used for updating the state
8. Creating the view/ UI
Now that we have created all the required entities such as Store, Actions, and Reducers, it's time to create the UI elements. Create a component
folder inside src
and create a Cart.js
file inside it. Add the following line inside Cart.js
import React from "react";
const Cart = () => {
return (
<div className="cart">
<h2>Number of items in Cart:</h2>
<button className="green">Add Item to Cart</button>
<button className="red">Remove Item from Cart</button>
</div>
);
};
export default Cart;
Add this Cart
component in the App.js
import "./App.css";
import { Provider } from "react-redux";
import store from "./store";
import Cart from "./component/Cart";
function App() {
return (
<Provider store={store}>
<Cart />
</Provider>
);
}
export default App;
Just to make it a bit presentable, I have added a bit of basic styling in the App.css
as follows.
button {
margin: 10px;
font-size: 16px;
letter-spacing: 2px;
font-weight: 400;
color: #fff;
padding: 23px 50px;
text-align: center;
display: inline-block;
text-decoration: none;
border: 0px;
cursor: pointer;
}
.green {
background-color: rgb(6, 172, 0);
}
.red {
background-color: rgb(221, 52, 66);
}
.red:disabled {
background-color: rgb(193, 191, 191);
cursor: not-allowed;
}
.cart {
text-align: center;
}
This is how the UI looks as of now
9. Reading/ Accessing the store using useSelector
hook
useSelector
is a hook provided by the react-redux library that helps us in reading the store
and thus its content(s). Import the hook from react-redux
and use the following syntax to read the store with useSelector
hook
import { useSelector } from "react-redux";
// rest of the code
const state = useSelector((state) => state);
// rest of the code
Thus after adding useSelector
hook, Cart.js
file will look some thing like this
import React from "react";
import { useSelector } from "react-redux";
const Cart = () => {
const state = useSelector((state) => state);
console.log("store", state);
return (
<div className="cart">
<h2>Number of items in Cart:</h2>
<button className="green">Add Item to Cart</button>
<button className="red">Remove Item from Cart</button>
</div>
);
};
export default Cart;
console logging the state will give us the initial state that we set in the reducer file in step 5.
10. Dispatching Action on button click (along with handling some UI behaviour based on the state) with useDispatch
hook
The react-redux library gives us another hook called the useDispatch
hook, that helps us dispatching the actions or action creators which in turn returns actions. The syntax is as follows
const dispatch = useDispatch();
dispatch(actionObject or calling the action creator);
Thus adding a dispatcher into our Cart.js
will finally make the file look some thing like this
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { addItem, deleteItem } from "../actions/cartAction";
const Cart = () => {
const state = useSelector((state) => state);
const dispatch = useDispatch();
return (
<div className="cart">
<h2>Number of items in Cart: {state.numOfItems}</h2>
<button
onClick={() => {
dispatch(addItem());
}}
>
Add Item to Cart
</button>
<button
disabled={state.numOfItems > 0 ? false : true}
onClick={() => {
dispatch(deleteItem());
}}
>
Remove Item to Cart
</button>
</div>
);
};
export default Cart;
Note how on click on the Add Item to Cart button, we dispatch
the action creator addItem()
that we created in step no. 7. Similarly on click on the Remove Item from Cart button, we dispatch the action creator deleteItem()
. The state
variable stores the state of the app, which is basically an object with a key numOfItems
. Thus state.numOfItems
gives us the current number of items value in the store. We display this in the view in the line <h2>Number of items in Cart: {state.numOfItems}</h2>
.
To dig a bit deeper, when the Add Item to Cart button is clicked, it dispatches the addItem()
action creator, which in turn returns an action
object with type type: ADD_ITEM
. As mentioned in Part 1 of this blog series, when an action is dispatched, all the reducers becomes active. Currently in this example we have only one reducer i.e. cartReducer
, thus it becomes active and listens to the action
dispatched. As shown in step 5, the reducer takes the state and the action as input, switches on the action type
and returns the fresh new instance of the updated state. In this example, when the action with type: ADD_ITEM
, matches the first switch case, it first make a copy of the entire state using spread operator ...state
, and then make the necessary update which in the case of adding item is numOfItems: state.numOfItems + 1
i.e. increasing the numOfItems
by 1.
Similarly, using the same logic, on clicking on the Remove Item from Cart button, an action with type type: DELETE_ITEM
is dispatched which goes and decreases the numOfItems
by 1.
Here is the demo of the working app.
Notice how we were able to control the behaviour of the Remove Item from Cart button based on the value of numOfItems
in the redux store. As a negative number of items does not makes sense, we disabled the Remove Item from Cart button if state.numOfItems <= 0
. This way we are able to restrict the user from decreasing the number of items in the cart if its already 0. This was a basic example to show how we can control the behaviour of various DOM elements based on internal state of the app.
Github Link
Github link of the project can be found here: Github Link
Summary
In this article, we learnt how to quickly spin up a redux powered react application. We learnt how to
- Create Actions, Action creators, Reducers and Store
- Provide the Store to the App using
<Provider>
- Read/ access the Store from components using
useSelector
hook and display the state information in the UI - Dispatch the actions on user events such as button clicks, using
useDispatch
hook - Control DOM element's behaviour with logic based on the state of the application
Wrapup
Thanks for reading! I really hope you enjoyed reading about how to spin up a redux powered react application and found this blog useful. Do consider hitting the like button and sharing it with your friends, I'd really appreciate that. Stay tuned for more amazing content! Peace out! ๐
Social Links
Posted on May 26, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.