Simplifying State Management with Redux in MERN Applications
Last Updated :
27 Mar, 2024
In this project, we've developed a todo web application utilizing the MERN stack (MongoDB, Express.js, React.js, Node.js) with Redux for state management. In this project, a todo can be created, viewed, and also saved in the database.
Output Preview: Let us have a look at how the final output will look like.
Final OutputPrerequisites:
Approach to Implement State Management in Todo App:
- Brainstorming on features : Before moving to coding part, brainstorm on its features is important. Its feature may be creation, save to local storage or database , display it etc.
- Creating backend : Set up a new Node.js project and install required package like express, mongoose etc. Connect backend to database and then make APIs.
- Creating Frontend : Initialize a react app. Make components like todo , addtodo and these files reside inside src directory.
- Manage state with redux : Make store, actions and reducers for managing and holding state to avoid props drilling.
- Connect frontend to backend : Fetching and storing data from frontend to backend with the help of APIs.
Steps to Create Backend:
Step 1 : Create a root directory named backend and navigate to it.
mkdir backend
cd backend
Steps 2 : Now initialize node.js project here.
npm init -y
Step 3 : Install required module and their dependencies.
npm i express mongoose cors dotenv
package.json:
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.3",
"mongoose": "^8.2.1"
}
Project Structure(Backend):
Backend Project StructureExample: Below is the code example for backend:
JavaScript
// server.js
const mongoose = require('mongoose')
const databaseConnection = () => {
mongoose.connect(process.env.MONGOOSE_URI)
console.log('Database connected successfully.')
return true;
}
// defining schema
const todosSchema = new mongoose.Schema({
id: String,
message: String,
date: String
})
// defining todo model
const Todo = mongoose.model('Todo', todosSchema)
module.exports = {
Todo,
databaseConnection
}
JavaScript
// app.js
const express = require('express')
const cors = require('cors')
const dotenv = require('dotenv')
const { Todo, databaseConnection } = require('./server')
const app = express()
dotenv.config()
// calling database
databaseConnection();
// calling middlewares
app.use(express.json())
app.use(cors())
const PORT = process.env.PORT || 8000
// fetching
app.get('/api/get', async (req, res) => {
try {
const todos = await Todo.find();
return res.json({ success: true, todos })
} catch (error) {
return res.json({ success: false, data: {}, error })
}
})
// saving todos
app.post('/api/save', async (req, res) => {
try {
const todos = req.body
let data = new Todo(todos[1])
await data.save();
return res.json({ success: true, data })
} catch (error) {
return res.json({ success: false, data: {}, error })
}
})
// listing server
app.listen(PORT, () => {
console.log(`Server is running on PORT : ${PORT}`)
})
Steps to Create Frontend:
Step 1 : Create a root directory named frontend and navigate to it.
npm frontend
cd frontend
Step 2 : Create a react app using this command.
npx create-react-app
Steps 3 : Install required package for this project.
npm i react-redux @reduxjs/toolkit cors
package.js:
"dependencies": {
"@reduxjs/toolkit": "^2.2.1",
"axios": "^1.6.7",
"react": "^18.2.0",
"react-redux": "^9.1.0",
"redux": "^5.0.1"
},
Project Structure:
Frontend Project Structure
Example: Below is the code example frontend:
CSS
/* style.css */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
#app_wrapper {
display: flex;
flex-direction: column;
align-items: center;
}
#input_box {
width: 400px;
border-radius: 10px 0px 0px 10px;
padding: 10px 20px;
border: 2px solid black;
border-right: none;
outline: none;
text-transform: capitalize;
}
#add_btn {
padding: 10px 20px;
border-radius: 0px 10px 10px 0px;
background-color: #44a075;
cursor: pointer;
color: white;
}
#todo_banner {
background-color: #44a075;
text-align: center;
padding: 10px 0px;
color: white;
font-weight: bolder;
width: 100vw;
}
#add_todo_wrapper {
margin: 20px 0px;
}
/* todos component css */
.todo {
display: flex;
border: 1px solid black;
padding: 5px 20px;
border-radius: 10px;
align-items: center;
gap: 20px;
width: 400px;
margin-bottom: 5px;
text-transform: capitalize;
justify-content: space-around;
}
.todo img {
width: 30px;
height: 30px;
border-radius: 50%;
}
#save_btn,
#get_todos_btn {
padding: 10px 20px;
border-radius: 10px;
background-color: #44a075;
cursor: pointer;
color: white;
border: 2px solid black;
margin: 5px;
}
JavaScript
// store.js
// creating slice
import { createSlice, configureStore } from "@reduxjs/toolkit";
// initial state of the todo slice
const initialState = {
todos: [
{
id: '',
message: '',
date: ''
}
]
}
const todoSlice = createSlice({
name: 'Todo Slice',
initialState,
reducers: {
addTodo: (state, action) => {
state.todos.push(action.payload)
},
}
})
export const { addTodo } = todoSlice.actions
// creating store
const store = configureStore({
reducer: todoSlice.reducer
})
export default store;
JavaScript
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import {
Todos, AddTodo,
GetDatabaseTodos, SaveToDatabase
} from './TodoComp'
import { Provider } from 'react-redux'
import './style.css'
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<div id='app_wrapper' >
<h2 id='todo_banner'>Your's Todo</h2>
<AddTodo />
<Todos />
<GetDatabaseTodos />
<SaveToDatabase />
</div>
</Provider>
);
JavaScript
// TodoComp.js
import React, { useState } from "react";
import axios from "axios";
import { useSelector, useDispatch } from "react-redux";
import { addTodo } from './store'
export function Todos() {
const todos = useSelector((state) => state.todos)
console.log(todos);
return (
<div id="todos_wrapper">
{
todos && todos.map((todo) => {
if (todo.message)
return (
<div className='todo' key={todo.id} >
<span>{todo.message}</span>
<span> <img src=
"https://media.geeksforgeeks.org/gfg-gg-logo.svg"
alt="gfg_logo" /> </span>
<span>{todo.date}</span>
</div>
)
})
}
</div >
)
}
export function GetDatabaseTodos() {
const dispatch = useDispatch()
const getTodosHandle = async () => {
try {
let todos =
await axios.get(
'https://gray-teacher-awrcf.pwskills.app:8000/api/get')
todos = todos.data.todos
console.log(todos);
todos.map((todo) => {
return dispatch(addTodo(
{
id: todo.id, message: todo.message,
date: todo.date
}))
})
} catch (error) {
return alert('Please try later.')
}
}
return (
<button id='get_todos_btn'
onClick={getTodosHandle}>
Get Database's Todos
</button>
)
}
export function SaveToDatabase() {
const todos = useSelector((state) => state.todos)
const saveHandle = async () => {
try {
await axios.post(
'https://gray-teacher-awrcf.pwskills.app:8000/api/save', todos)
return alert('Todos save successfully!')
} catch (error) {
return alert('Todos save failed!')
}
}
return (
<button id='save_btn'
onClick={saveHandle}>
Save To DataBase
</button>
)
}
export function AddTodo() {
const dispatch = useDispatch();
const [message, setMessage] = useState('')
const addTodoHandle = () => {
if (message.trim() === '') return
let date = new Date()
date = `${date.getDate()}-${date.getMonth()}-
${date.getFullYear()} ${date.getHours()}:
${date.getMinutes()}`
dispatch(addTodo(
{
id: Math.random().toFixed(5),
message, date
}))
setMessage('')
}
return (
<div id="add_todo_wrapper">
<input type="text" id="input_box"
value={message}
onChange={
(e) => setMessage(e.target.value)
} />
<button id="add_btn" onClick={addTodoHandle} >Add</button>
</div>
)
}
Steps to Run the Application:
To start Backend Server : Go to backed folder and follow this command to start backend server.
node ./app.js
To start Frontend Server : Go to frontend folder and follow this command to start frontend server.
npm start
Output:
Browser Output
Output of data saved in Database
Similar Reads
How Redux Toolkit simplifies Redux code in React application ?
Redux Toolkit is a powerful library designed to simplify the complexities of managing application state with Redux in React applications. At its core, Redux Toolkit provides developers with a set of utilities and abstractions that significantly reduce boilerplate code and streamline common Redux tas
5 min read
How to Normalize State in Redux Applications ?
In Redux applications, efficient state management is essential for scalability and maintainability. Normalization is a technique used to restructure complex state data into a more organized format, improving performance and simplifying state manipulation. This article covers the concept of normaliza
3 min read
State Management in React: Context API vs. Redux vs. Recoil
A fundamental idea in application development, State management is especially important for contemporary online and mobile apps, where dynamic, interactive user experiences necessitate frequent modifications to data and interface components. Fundamentally, it describes how an application maintains a
12 min read
State Management in React â Hooks, Context API and Redux
State management is a critical concept when working with React. React components can hold local state, but as applications grow, managing state across multiple components can become complex. To help manage this complexity, React provides several tools: Hooks, Context API, and Redux. Here are some fe
6 min read
How to Manage User Sessions and Tokens in Redux Applications?
This project is designed to provide a basic user authentication system entirely on the client side using React and Redux, allowing users to login, register, and logout. Authentication actions and state management are managed locally, with user data stored in the browser's local storage. ApproachProj
4 min read
State management using Redux in Flutter
State management is a crucial aspect of building dynamic and responsive applications. While building applications, we sometimes need to maintain a state between multiple screens. Here comes the part of State Management. There are different state management techniques in Flutter like GetX, Provider,
6 min read
How to manage global state in a React application?
Global state refers to data that is accessible across multiple components in a React application. Unlike the local state, which is confined to a single component, the global state can be accessed and modified from anywhere in the component tree. In this article, we will explore the following approac
7 min read
What are optimistic updates in Redux applications?
Optimistic updates in Redux applications mean making changes to the user interface as if everything went smoothly, even before the server confirms it. It's like assuming that your action, such as posting a comment or liking a post, will succeed without waiting for the server's response. This approac
2 min read
How do you handle real-time updates in Redux applications?
Handling real-time updates is essential for modern web applications to provide users with dynamic and interactive experiences. In Redux applications, managing real-time updates efficiently involves implementing strategies to synchronize the application state with changes occurring on the server in r
5 min read
Mastering State Management in ReactJS: A Beginner's Guide to the Context API
State Management in React.js is an essential topic as the whole of React counters & controllers work through their states. State is the initial value of the component, or the current value & using state management we can change the value by some specific functionalities using react.js. One s
10 min read