Let’s create a fully functional React CRUD app using Redux Toolkit, we need to follow these steps. Redux Toolkit simplifies the setup of Redux by providing efficient tools and practices out-of-the-box.
Step 1: Setting Up the React Project
First, create a new React app:
npx create-react-app react-crud-app
cd react-crud-app
npm install axios react-router-dom@6 @reduxjs/toolkit react-redux
let’s break down each of these commands and understand what they do step by step.
npx create-react-app react-crud-app
Command Breakdown
1. Create a New React Application
npx
: A package runner tool that comes with npm (node package manager) 5.2.0 and higher. It is used to execute packages without having to install them globally.create-react-app
: A tool that sets up a new React project with a sensible default configuration.react-crud-app
: The name of the new React application. This will create a directory calledreact-crud-app
containing the new application.
When you run this command, create-react-app
will:
- Create a new directory named
react-crud-app
. - Generate a new React application with a default structure, including essential files and configurations.
- Install all necessary dependencies for a basic React application, such as
react
,react-dom
, andreact-scripts
.
cd react-crud-app
2. Navigate into the Project Directory
cd
: Change directory command. It changes the current working directory to the specified path.react-crud-app
: The directory created bycreate-react-app
in the previous step. This command navigates into thereact-crud-app
directory, so you can start working on your new React project.
npm install axios react-router-dom@6 @reduxjs/toolkit react-redux
3. Install Additional Packages
npm install
: The npm command to install one or more packages and add them to thenode_modules
directory and thepackage.json
file.axios
: A popular HTTP client for making requests to external APIs. It is promise-based and works both in the browser and in Node.js environments.react-router-dom@6
: A package for handling routing in React applications. The@6
specifies that version 6 of the package should be installed, which introduces a lot of changes and improvements over previous versions.@reduxjs/toolkit
: The official, recommended way to write Redux logic. It provides a set of tools to simplify Redux development and avoid common pitfalls.react-redux
: Official React bindings for Redux. It allows React components to interact with a Redux store.
Summary
Create a New React Application:
npx create-react-app react-crud-app
: This sets up a new React project namedreact-crud-app
with all the necessary configurations and dependencies.
Navigate to the Project Directory:
cd react-crud-app
: Changes the current working directory to the newly createdreact-crud-app
directory.
Install Additional Packages:
npm install axios react-router-dom@6 @reduxjs/toolkit react-redux
: Installs additional packages needed for the project:axios
for making HTTP requests.react-router-dom
for handling routing in the React application.@reduxjs/toolkit
for managing state using Redux with simplified configuration and development patterns.react-redux
for integrating Redux with React components.
These steps collectively set up a new React project with routing, state management, and the ability to make HTTP requests, providing a robust foundation for developing a CRUD (Create, Read, Update, Delete) application.
Step 2: Setting Up JSON Server
Create a db.json
file in the root of your project:
{
"employees": [
{ "id": 1, "firstName": "Ramesh", "lastName": "Fadatare", "email": "ram@gmail.com" },
{ "id": 2, "firstName": "John", "lastName": "Cena", "email": "john@gmail.com" },
{ "id": 3, "firstName": "Tom", "lastName": "Cruise", "email": "tom@gmail.com" },
{ "id": 4, "firstName": "Admin", "lastName": "admin", "email": "admin@gmail.com" }
]
}
Install and run json-server:
npm install -g json-server
json-server --watch db.json --port 5000
Let’s break down these two commands step by step.
Command 1: npm install -g json-server
Explanation
npm
: Node Package Manager, a package manager for JavaScript, commonly used to install and manage packages for Node.js.install
: A command used to install packages.-g
: A flag that stands for “global”. It means that the package will be installed globally, making it accessible from any directory on your system.json-server
: The name of the package to be installed.json-server
is a tool that allows you to quickly create a mock REST API based on a JSON file.
Purpose
This command installs json-server
globally on your machine, allowing you to use it from any directory. json-server
can create a full fake REST API with zero coding in less than 30 seconds.
Command 2: json-server --watch db.json --port 5000
Explanation
json-server
: The command to start the JSON server. This command is available becausejson-server
was installed globally in the previous step.--watch db.json
: A flag and argument tellingjson-server
to watch thedb.json
file. This file contains the data that will be served by the API.--watch
: Tellsjson-server
to monitor the specified file for changes and automatically reload the server when changes are detected.db.json
: The JSON file that contains the mock data for the API.
--port 5000
: A flag and argument specifying the port on which the server should run. In this case, the server will be accessible athttp://localhost:5000
.
Purpose
This command starts the JSON server and watches the db.json
file for changes. The server runs on port 5000 and provides a RESTful API based on the contents of db.json
. This allows you to:
- Create and test API endpoints quickly without needing to set up a full backend server.
- Simulate different responses from the API by modifying the
db.json
file.
Step 3: Creating Redux Store and Slice
Create the Redux store and slices for managing employees.
src/app/store.js:
import { configureStore } from '@reduxjs/toolkit';
import employeeReducer from '../features/employees/employeeSlice';
export const store = configureStore({
reducer: {
employees: employeeReducer
},
});
export default store;
Let’s break down the code snippet step by step to understand how it works and what each part does.
import { configureStore } from '@reduxjs/toolkit';
import employeeReducer from '../features/employees/employeeSlice';
Import Statements
configureStore
: This is a function from Redux Toolkit that simplifies the process of setting up a Redux store. It comes with good default settings and is designed to reduce the amount of boilerplate code needed.employeeReducer
: This is a reducer function imported from theemployeeSlice
module. A reducer is a function that takes the current state and an action as arguments and returns a new state. It typically handles updates to a specific part of the application’s state.
export const store = configureStore({
reducer: {
employees: employeeReducer
},
});
Store Configuration
configureStore
: This function is used to create the Redux store. It accepts a configuration object with several options, includingreducer
.reducer
: This property specifies the reducers for the Redux store. It’s an object where each key is a slice of the state and the value is the corresponding reducer function.employees
: This key will be the name of the slice of state managed byemployeeReducer
.employeeReducer
: The reducer function imported earlier. It will handle actions related to the employees’ state.
export default store;
Exporting the Store
- This line exports the configured Redux store so that it can be used throughout the application. By exporting the store, you can import it in your main application file and provide it to your React application using the
Provider
component fromreact-redux
.
Summary
Imports:
configureStore
from@reduxjs/toolkit
to set up the Redux store.employeeReducer
fromemployeeSlice
to handle the employees’ state.
Configuring the Store:
- Use
configureStore
to create the Redux store. - The
reducer
property is an object that defines the different slices of state and their corresponding reducers. In this case, it has a single slice namedemployees
managed byemployeeReducer
.
- Use
Exporting the Store:
- Export the configured store for use in your application.
This setup allows your React application to manage state using Redux Toolkit, with the employeeReducer
handling all actions related to the employees’ state. The store is then provided to the entire application, allowing components to access and interact with the state.
src/features/employees/employeeSlice.js:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
// Async thunks for API calls
export const fetchEmployees = createAsyncThunk('employees/fetchEmployees', async () => {
const response = await axios.get('http://localhost:5000/employees');
return response.data;
});
export const addEmployee = createAsyncThunk('employees/addEmployee', async (newEmployee) => {
const response = await axios.post('http://localhost:5000/employees', newEmployee);
return response.data;
});
export const updateEmployee = createAsyncThunk('employees/updateEmployee', async ({ id, updatedEmployee }) => {
const response = await axios.put(`http://localhost:5000/employees/${id}`, updatedEmployee);
return response.data;
});
export const deleteEmployee = createAsyncThunk('employees/deleteEmployee', async (id) => {
await axios.delete(`http://localhost:5000/employees/${id}`);
return id;
});
const employeeSlice = createSlice({
name: 'employees',
initialState: {
employees: [],
status: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchEmployees.fulfilled, (state, action) => {
state.employees = action.payload;
})
.addCase(addEmployee.fulfilled, (state, action) => {
state.employees.push(action.payload);
})
.addCase(updateEmployee.fulfilled, (state, action) => {
const index = state.employees.findIndex(emp => emp.id === action.payload.id);
state.employees[index] = action.payload;
})
.addCase(deleteEmployee.fulfilled, (state, action) => {
state.employees = state.employees.filter(emp => emp.id !== action.payload);
});
},
});
export default employeeSlice.reducer;
Let’s break down this code step by step to understand how it sets up the Redux slice and handles asynchronous operations with Redux Toolkit.
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
Import Statements
createSlice
: A function from Redux Toolkit that simplifies creating Redux slices (a slice of the state with its actions and reducers).createAsyncThunk
: A function from Redux Toolkit for handling asynchronous logic and automatically generating pending, fulfilled, and rejected action types.axios
: A promise-based HTTP client used to make API requests.
export const fetchEmployees = createAsyncThunk('employees/fetchEmployees', async () => {
const response = await axios.get('http://localhost:5000/employees');
return response.data;
});
Async Thunks for API Calls
fetchEmployees
fetchEmployees
: This thunk fetches all employees from the API.createAsyncThunk('employees/fetchEmployees', async () => { ... })
: This function generates the thunk and automatically handles dispatchingpending
,fulfilled
, andrejected
actions based on the promise lifecycle.- API Request: It makes a GET request to
http://localhost:5000/employees
and returns the response data.
export const addEmployee = createAsyncThunk('employees/addEmployee', async (newEmployee) => {
const response = await axios.post('http://localhost:5000/employees', newEmployee);
return response.data;
});
addEmployee
addEmployee
: This thunk adds a new employee.- API Request: It makes a POST request to
http://localhost:5000/employees
withnewEmployee
data and returns the response data.
export const updateEmployee = createAsyncThunk('employees/updateEmployee', async ({ id, updatedEmployee }) => {
const response = await axios.put(`http://localhost:5000/employees/${id}`, updatedEmployee);
return response.data;
});
updateEmployee
updateEmployee
: This thunk updates an existing employee.- API Request: It makes a PUT request to
http://localhost:5000/employees/${id}
withupdatedEmployee
data and returns the response data.
export const deleteEmployee = createAsyncThunk('employees/deleteEmployee', async (id) => {
await axios.delete(`http://localhost:5000/employees/${id}`);
return id;
});
deleteEmployee
deleteEmployee
: This thunk deletes an employee by ID.- API Request: It makes a DELETE request to
http://localhost:5000/employees/${id}
and returns the ID of the deleted employee.
const employeeSlice = createSlice({
name: 'employees',
initialState: {
employees: [],
status: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchEmployees.fulfilled, (state, action) => {
state.employees = action.payload;
})
.addCase(addEmployee.fulfilled, (state, action) => {
state.employees.push(action.payload);
})
.addCase(updateEmployee.fulfilled, (state, action) => {
const index = state.employees.findIndex(emp => emp.id === action.payload.id);
state.employees[index] = action.payload;
})
.addCase(deleteEmployee.fulfilled, (state, action) => {
state.employees = state.employees.filter(emp => emp.id !== action.payload);
});
},
});
export default employeeSlice.reducer;
Employee Slice
createSlice
Configuration
name
: The name of the slice, in this case,employees
.initialState
: The initial state of the slice, with an emptyemployees
array andstatus
set tonull
.reducers
: An empty object here because the slice does not define any synchronous actions. All logic is handled inextraReducers
.extraReducers
: A function to handle actions generated by the thunks.
extraReducers
Details
fetchEmployees.fulfilled
: Updates the state with the list of employees fetched from the API.addEmployee.fulfilled
: Adds a new employee to the state.updateEmployee.fulfilled
: Updates an existing employee in the state.deleteEmployee.fulfilled
: Removes an employee from the state by filtering out the employee with the given ID.
Summary
createSlice
: Simplifies creating Redux slices with reducers and actions.createAsyncThunk
: Manages asynchronous actions and dispatchespending
,fulfilled
, andrejected
actions automatically.axios
: Used to make HTTP requests to the API.- Thunks:
fetchEmployees
,addEmployee
,updateEmployee
, anddeleteEmployee
handle API interactions. - Slice: The
employeeSlice
manages the state for employees, handling updates when thunks are fulfilled.
This setup allows for a clean, organized way to manage asynchronous actions and state updates in a Redux application.
Step 4: Creating Components
Create the necessary component files and use the Redux store to manage state.
src/components/EmployeeList.js:
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchEmployees } from '../features/employees/employeeSlice';
import { Link } from 'react-router-dom';
const EmployeeList = () => {
const dispatch = useDispatch();
const employees = useSelector(state => state.employees.employees);
useEffect(() => {
dispatch(fetchEmployees());
}, [dispatch]);
return (
<div className="employee-list">
<h2>Employees List</h2>
<Link to="/add" className="btn btn-primary">Add Employee</Link>
<table className="table">
<thead>
<tr>
<th>Employee First Name</th>
<th>Employee Last Name</th>
<th>Employee Email Id</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{employees.map(employee => (
<tr key={employee.id}>
<td>{employee.firstName}</td>
<td>{employee.lastName}</td>
<td>{employee.email}</td>
<td>
<Link to={`/update/${employee.id}`} className="btn btn-info">Update</Link>
<Link to={`/delete/${employee.id}`} className="btn btn-danger">Delete</Link>
<Link to={`/view/${employee.id}`} className="btn btn-success">View</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default EmployeeList;
Let’s break down the EmployeeList
component step-by-step to understand how it functions within the context of a React and Redux application.
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchEmployees } from '../features/employees/employeeSlice';
import { Link } from 'react-router-dom';
Import Statements
- React: The core React library.
- useEffect: A React hook that runs side effects in functional components.
- useDispatch: A hook from
react-redux
to dispatch actions to the Redux store. - useSelector: A hook from
react-redux
to access the Redux store’s state. - fetchEmployees: An asynchronous thunk action to fetch employees from an API.
- Link: A component from
react-router-dom
used for navigation within the application.
const dispatch = useDispatch();
const employees = useSelector(state => state.employees.employees);
Component Definition
The EmployeeList
component is defined as a functional component.
Using Hooks
- useDispatch: Returns a reference to the
dispatch
function from the Redux store. This function is used to dispatch actions. - useSelector: Takes a selector function as an argument and returns the selected state. In this case, it retrieves the
employees
array from the Redux store’s state.
useEffect(() => {
dispatch(fetchEmployees());
}, [dispatch]);
useEffect Hook
- useEffect: This hook runs when the component mounts. The empty dependency array
[]
means it runs only once. - dispatch(fetchEmployees()): Dispatches the
fetchEmployees
thunk action to fetch employee data from the API when the component mounts.
return (
<div className="employee-list">
<h2>Employees List</h2>
<Link to="/add" className="btn btn-primary">Add Employee</Link>
<table className="table">
<thead>
<tr>
<th>Employee First Name</th>
<th>Employee Last Name</th>
<th>Employee Email Id</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{employees.map(employee => (
<tr key={employee.id}>
<td>{employee.firstName}</td>
<td>{employee.lastName}</td>
<td>{employee.email}</td>
<td>
<Link to={`/update/${employee.id}`} className="btn btn-info">Update</Link>
<Link to={`/delete/${employee.id}`} className="btn btn-danger">Delete</Link>
<Link to={`/view/${employee.id}`} className="btn btn-success">View</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
JSX Template
- Container: The main content is wrapped in a
div
with the classemployee-list
. - Heading: Displays “Employees List”.
- Link to Add Employee: A
Link
component to navigate to the “Add Employee” page. - Table: Displays the list of employees in a table format.
- Table Header: Defines the columns for first name, last name, email, and actions.
- Table Body: Maps over the
employees
array and renders a row for each employee.- Table Row: Each row displays the employee’s first name, last name, and email.
- Action Links: Links for updating, deleting, and viewing details of an employee.
Summary
Imports:
- Essential libraries and hooks (
React
,useEffect
,useDispatch
,useSelector
,Link
). fetchEmployees
thunk action from theemployeeSlice
.
- Essential libraries and hooks (
Component Definition:
- useDispatch: To dispatch actions.
- useSelector: To access the
employees
state from the Redux store.
useEffect:
- Dispatches the
fetchEmployees
action when the component mounts to fetch employee data from the API.
- Dispatches the
Rendering:
- Displays a list of employees in a table with action links for each employee (Update, Delete, View).
- Includes a link to the “Add Employee” page.
src/components/AddEmployee.js:
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { addEmployee } from '../features/employees/employeeSlice';
const AddEmployee = () => {
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
const dispatch = useDispatch();
const navigate = useNavigate();
const handleChange = (e) => {
const { name, value } = e.target;
setEmployee({ ...employee, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addEmployee(employee));
navigate('/');
};
return (
<div className="add-employee">
<h2>Add Employee</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" name="firstName" value={employee.firstName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value={employee.lastName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" name="email" value={employee.email} onChange={handleChange} required />
</div>
<button type="submit" className="btn btn-primary">Add</button>
</form>
</div>
);
};
export default AddEmployee;
Let’s break down the AddEmployee
component step-by-step to understand its functionality and how it works within a React and Redux application.
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { addEmployee } from '../features/employees/employeeSlice';
Import Statements
- React: The core React library for building user interfaces.
- useState: A React hook to manage the component’s state.
- useDispatch: A hook from
react-redux
to dispatch actions to the Redux store. - useNavigate: A hook from
react-router-dom
to navigate programmatically. - addEmployee: The action creator thunk to add a new employee, imported from the employee slice.
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
const dispatch = useDispatch();
const navigate = useNavigate();
Component Definition
The AddEmployee
component is defined as a functional component.
State and Hooks
employee
: State to hold the new employee’s data (first name, last name, email).setEmployee
: Function to update theemployee
state.dispatch
: Function to dispatch actions to the Redux store.navigate
: Function to navigate to different routes programmatically.
const handleChange = (e) => {
const { name, value } = e.target;
setEmployee({ ...employee, [name]: value });
};
Handling Input Changes
- handleChange: Updates the
employee
state whenever an input field changes. The input field’sname
attribute is used to determine which field to update.
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addEmployee(employee));
navigate('/');
};
Handling Form Submission
- handleSubmit: Prevents the default form submission behavior, dispatches the
addEmployee
action with the currentemployee
state, and navigates back to the home page (/
) upon successful submission.
return (
<div className="add-employee">
<h2>Add Employee</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" name="firstName" value={employee.firstName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value={employee.lastName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" name="email" value={employee.email} onChange={handleChange} required />
</div>
<button type="submit" className="btn btn-primary">Add</button>
</form>
</div>
);
JSX Template
- Container: The main content is wrapped in a
div
with the classadd-employee
. - Heading: Displays “Add Employee”.
- Form: A form to capture the new employee’s details.
- Form Groups: Each group contains a label and an input field for first name, last name, and email.
- Input Fields: Controlled components that update the
employee
state on change. - Submit Button: A button to submit the form and add the new employee.
src/components/UpdateEmployee.js:
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { updateEmployee } from '../features/employees/employeeSlice';
const UpdateEmployee = () => {
const { id } = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
const employees = useSelector(state => state.employees.employees);
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
useEffect(() => {
const currentEmployee = employees.find(emp => emp.id === parseInt(id));
if (currentEmployee) {
setEmployee(currentEmployee);
}
}, [id, employees]);
const handleChange = (e) => {
const { name, value } = e.target;
setEmployee({ ...employee, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
dispatch(updateEmployee({ id, updatedEmployee: employee }));
navigate('/');
};
return (
<div className="update-employee">
<h2>Update Employee</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" name="firstName" value={employee.firstName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value={employee.lastName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" name="email" value={employee.email} onChange={handleChange} required />
</div>
<button type="submit" className="btn btn-primary">Update</button>
</form>
</div>
);
};
export default UpdateEmployee;
Let’s break down the UpdateEmployee
component step-by-step to understand its functionality and how it integrates with React and Redux.
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { updateEmployee } from '../features/employees/employeeSlice';
Import Statements
- React: The core React library for building user interfaces.
- useState: A React hook to manage the component’s local state.
- useEffect: A React hook to handle side effects in functional components.
- useDispatch: A hook from
react-redux
to dispatch actions to the Redux store. - useSelector: A hook from
react-redux
to access the Redux store’s state. - useParams: A hook from
react-router-dom
to access URL parameters. - useNavigate: A hook from
react-router-dom
to programmatically navigate between routes. - updateEmployee: The action creator thunk to update an existing employee, imported from the employee slice.
const { id } = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
const employees = useSelector(state => state.employees.employees);
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
Component Definition
The UpdateEmployee
component is defined as a functional component.
State and Hooks
id
: Extracted from the URL usinguseParams
. This is the ID of the employee to be updated.dispatch
: A function to dispatch actions to the Redux store.navigate
: A function to navigate programmatically.employees
: Accesses theemployees
slice from the Redux store.employee
: Local state to hold the current employee’s data being edited.setEmployee
: Function to update theemployee
state.
useEffect(() => {
const currentEmployee = employees.find(emp => emp.id === parseInt(id));
if (currentEmployee) {
setEmployee(currentEmployee);
}
}, [id, employees]);
useEffect Hook
- useEffect: Runs when the component mounts or when the
id
oremployees
changes. It finds the employee with the matchingid
from theemployees
array and sets theemployee
state with this employee’s data. employees.find(emp => emp.id === parseInt(id))
: Searches for the employee with the givenid
.setEmployee(currentEmployee)
: Updates the local state with the current employee’s data.
const handleChange = (e) => {
const { name, value } = e.target;
setEmployee({ ...employee, [name]: value });
};
Handling Input Changes
- handleChange: Updates the
employee
state whenever an input field changes. The input field’sname
attribute is used to determine which field to update.
const handleSubmit = (e) => {
e.preventDefault();
dispatch(updateEmployee({ id, updatedEmployee: employee }));
navigate('/');
};
Handling Form Submission
- handleSubmit: Prevents the default form submission behavior, dispatches the
updateEmployee
action with the currentemployee
state, and navigates back to the home page (/
) upon successful submission..
return (
<div className="update-employee">
<h2>Update Employee</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" name="firstName" value={employee.firstName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value={employee.lastName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" name="email" value={employee.email} onChange={handleChange} required />
</div>
<button type="submit" className="btn btn-primary">Update</button>
</form>
</div>
);
JSX Template
- Container: The main content is wrapped in a
div
with the classupdate-employee
. - Heading: Displays “Update Employee”.
- Form: A form to capture the updated employee’s details.
- Form Groups: Each group contains a label and an input field for first name, last name, and email.
- Input Fields: Controlled components that update the
employee
state on change. - Submit Button: A button to submit the form and update the employee.
Summary
Imports:
- Essential libraries and hooks (
React
,useState
,useEffect
,useDispatch
,useSelector
,useParams
,useNavigate
). updateEmployee
thunk action from theemployeeSlice
.
- Essential libraries and hooks (
Component Definition:
- useParams: Extracts the employee ID from the URL.
- useDispatch: Dispatches actions to the Redux store.
- useNavigate: Navigates programmatically after updating an employee.
- useSelector: Accesses the
employees
state from the Redux store. - useState: Manages the form state.
useEffect:
- Sets the local
employee
state when the component mounts or whenid
oremployees
changes.
- Sets the local
Event Handlers:
- handleChange: Updates the form state on input changes.
- handleSubmit: Dispatches the
updateEmployee
action and navigates back to the home page upon form submission.
Rendering:
- Displays a form with fields for first name, last name, and email, and a submit button to update the employee.
src/components/ViewEmployee.js:
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useSelector } from 'react-redux';
import axios from 'axios';
const ViewEmployee = () => {
const { id } = useParams();
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
const employees = useSelector(state => state.employees.employees);
useEffect(() => {
const currentEmployee = employees.find(emp => emp.id === parseInt(id));
if (currentEmployee) {
setEmployee(currentEmployee);
} else {
axios.get(`http://localhost:5000/employees/${id}`)
.then(response => {
setEmployee(response.data);
})
.catch(error => console.log(error));
}
}, [id, employees]);
return (
<div className="view-employee">
<h2>View Employee</h2>
<p>First Name: {employee.firstName}</p>
<p>Last Name: {employee.lastName}</p>
<p>Email: {employee.email}</p>
</div>
);
};
export default ViewEmployee;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
src/components/DeleteEmployee.js:
src/components/DeleteEmployee.js:
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { deleteEmployee } from '../features/employees/employeeSlice';
const DeleteEmployee = () => {
const { id } = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
useEffect(() => {
dispatch(deleteEmployee(id));
navigate('/');
}, [id, dispatch, navigate]);
return (
<div className="delete-employee">
<h2>Deleting Employee...</h2>
</div>
);
};
export default DeleteEmployee;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
src/components/Navbar.js:
import React from 'react';
import { Link } from 'react-router-dom';
const Navbar = () => {
return (
<nav className="navbar">
<h1>Employee Management App</h1>
<div className="links">
<Link to="/">Home</Link>
<Link to="/add">Add Employee</Link>
</div>
</nav>
);
};
export default Navbar;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Step 5: Creating the Main App Component
Update the main App.js
file to use the Redux Provider and include the necessary routes.
src/App.js:
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { Provider } from 'react-redux';
import Navbar from './components/Navbar';
import EmployeeList from './components/EmployeeList';
import AddEmployee from './components/AddEmployee';
import UpdateEmployee from './components/UpdateEmployee';
import ViewEmployee from './components/ViewEmployee';
import DeleteEmployee from './components/DeleteEmployee';
import store from './app/store';
import './App.css';
function App() {
return (
<Provider store={store}>
<Router>
<div className="App">
<Navbar />
<div className="content">
<Routes>
<Route exact path="/" element={<EmployeeList />} />
<Route path="/add" element={<AddEmployee />} />
<Route path="/update/:id" element={<UpdateEmployee />} />
<Route path="/view/:id" element={<ViewEmployee />} />
<Route path="/delete/:id" element={<DeleteEmployee />} />
</Routes>
</div>
</div>
</Router>
</Provider>
);
}
export default App;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Step 6: Styling the App
Ensure the CSS in App.css
matches the design provided in the image.
src/App.css:
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.navbar {
background-color: #333;
color: #fff;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.navbar h1 {
margin: 0;
}
.navbar .links a {
color: #fff;
margin-left: 20px;
text-decoration: none;
}
.btn {
padding: 5px 10px;
margin: 0 5px;
border: none;
color: #fff;
cursor: pointer;
}
.btn-primary {
background-color: #007bff;
}
.btn-info {
background-color: #17a2b8;
}
.btn-danger {
background-color: #dc3545;
}
.btn-success {
background-color: #28a745;
}
.table {
width: 100%;
margin: 20px 0;
border-collapse: collapse;
}
.table th, .table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.table th {
background-color: #f8f8f8;
}
.form-group {
margin: 10px 0;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
// Async thunks for API calls
export const fetchEmployees = createAsyncThunk('employees/fetchEmployees', async () => {
const response = await axios.get('http://localhost:5000/employees');
return response.data;
});
export const addEmployee = createAsyncThunk('employees/addEmployee', async (newEmployee) => {
const response = await axios.post('http://localhost:5000/employees', newEmployee);
return response.data;
});
export const updateEmployee = createAsyncThunk('employees/updateEmployee', async ({ id, updatedEmployee }) => {
const response = await axios.put(`http://localhost:5000/employees/${id}`, updatedEmployee);
return response.data;
});
export const deleteEmployee = createAsyncThunk('employees/deleteEmployee', async (id) => {
await axios.delete(`http://localhost:5000/employees/${id}`);
return id;
});
const employeeSlice = createSlice({
name: 'employees',
initialState: {
employees: [],
status: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchEmployees.fulfilled, (state, action) => {
state.employees = action.payload;
})
.addCase(addEmployee.fulfilled, (state, action) => {
state.employees.push(action.payload);
})
.addCase(updateEmployee.fulfilled, (state, action) => {
const index = state.employees.findIndex(emp => emp.id === action.payload.id);
state.employees[index] = action.payload;
})
.addCase(deleteEmployee.fulfilled, (state, action) => {
state.employees = state.employees.filter(emp => emp.id !== action.payload);
});
},
});
export default employeeSlice.reducer;
Let’s break down this code step by step to understand how it sets up the Redux slice and handles asynchronous operations with Redux Toolkit.
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
Import Statements
createSlice
: A function from Redux Toolkit that simplifies creating Redux slices (a slice of the state with its actions and reducers).createAsyncThunk
: A function from Redux Toolkit for handling asynchronous logic and automatically generating pending, fulfilled, and rejected action types.axios
: A promise-based HTTP client used to make API requests.
export const fetchEmployees = createAsyncThunk('employees/fetchEmployees', async () => {
const response = await axios.get('http://localhost:5000/employees');
return response.data;
});
Async Thunks for API Calls
fetchEmployees
fetchEmployees
: This thunk fetches all employees from the API.createAsyncThunk('employees/fetchEmployees', async () => { ... })
: This function generates the thunk and automatically handles dispatchingpending
,fulfilled
, andrejected
actions based on the promise lifecycle.- API Request: It makes a GET request to
http://localhost:5000/employees
and returns the response data.
export const addEmployee = createAsyncThunk('employees/addEmployee', async (newEmployee) => {
const response = await axios.post('http://localhost:5000/employees', newEmployee);
return response.data;
});
addEmployee
addEmployee
: This thunk adds a new employee.- API Request: It makes a POST request to
http://localhost:5000/employees
withnewEmployee
data and returns the response data.
export const updateEmployee = createAsyncThunk('employees/updateEmployee', async ({ id, updatedEmployee }) => {
const response = await axios.put(`http://localhost:5000/employees/${id}`, updatedEmployee);
return response.data;
});
updateEmployee
updateEmployee
: This thunk updates an existing employee.- API Request: It makes a PUT request to
http://localhost:5000/employees/${id}
withupdatedEmployee
data and returns the response data.
export const deleteEmployee = createAsyncThunk('employees/deleteEmployee', async (id) => {
await axios.delete(`http://localhost:5000/employees/${id}`);
return id;
});
deleteEmployee
deleteEmployee
: This thunk deletes an employee by ID.- API Request: It makes a DELETE request to
http://localhost:5000/employees/${id}
and returns the ID of the deleted employee.
const employeeSlice = createSlice({
name: 'employees',
initialState: {
employees: [],
status: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchEmployees.fulfilled, (state, action) => {
state.employees = action.payload;
})
.addCase(addEmployee.fulfilled, (state, action) => {
state.employees.push(action.payload);
})
.addCase(updateEmployee.fulfilled, (state, action) => {
const index = state.employees.findIndex(emp => emp.id === action.payload.id);
state.employees[index] = action.payload;
})
.addCase(deleteEmployee.fulfilled, (state, action) => {
state.employees = state.employees.filter(emp => emp.id !== action.payload);
});
},
});
export default employeeSlice.reducer;
Employee Slice
createSlice
Configuration
name
: The name of the slice, in this case,employees
.initialState
: The initial state of the slice, with an emptyemployees
array andstatus
set tonull
.reducers
: An empty object here because the slice does not define any synchronous actions. All logic is handled inextraReducers
.extraReducers
: A function to handle actions generated by the thunks.
extraReducers
Details
fetchEmployees.fulfilled
: Updates the state with the list of employees fetched from the API.addEmployee.fulfilled
: Adds a new employee to the state.updateEmployee.fulfilled
: Updates an existing employee in the state.deleteEmployee.fulfilled
: Removes an employee from the state by filtering out the employee with the given ID.
Summary
createSlice
: Simplifies creating Redux slices with reducers and actions.createAsyncThunk
: Manages asynchronous actions and dispatchespending
,fulfilled
, andrejected
actions automatically.axios
: Used to make HTTP requests to the API.- Thunks:
fetchEmployees
,addEmployee
,updateEmployee
, anddeleteEmployee
handle API interactions. - Slice: The
employeeSlice
manages the state for employees, handling updates when thunks are fulfilled.
This setup allows for a clean, organized way to manage asynchronous actions and state updates in a Redux application.
Step 4: Creating Components
Create the necessary component files and use the Redux store to manage state.
src/components/EmployeeList.js:
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchEmployees } from '../features/employees/employeeSlice';
import { Link } from 'react-router-dom';
const EmployeeList = () => {
const dispatch = useDispatch();
const employees = useSelector(state => state.employees.employees);
useEffect(() => {
dispatch(fetchEmployees());
}, [dispatch]);
return (
<div className="employee-list">
<h2>Employees List</h2>
<Link to="/add" className="btn btn-primary">Add Employee</Link>
<table className="table">
<thead>
<tr>
<th>Employee First Name</th>
<th>Employee Last Name</th>
<th>Employee Email Id</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{employees.map(employee => (
<tr key={employee.id}>
<td>{employee.firstName}</td>
<td>{employee.lastName}</td>
<td>{employee.email}</td>
<td>
<Link to={`/update/${employee.id}`} className="btn btn-info">Update</Link>
<Link to={`/delete/${employee.id}`} className="btn btn-danger">Delete</Link>
<Link to={`/view/${employee.id}`} className="btn btn-success">View</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default EmployeeList;
Let’s break down the EmployeeList
component step-by-step to understand how it functions within the context of a React and Redux application.
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchEmployees } from '../features/employees/employeeSlice';
import { Link } from 'react-router-dom';
Import Statements
- React: The core React library.
- useEffect: A React hook that runs side effects in functional components.
- useDispatch: A hook from
react-redux
to dispatch actions to the Redux store. - useSelector: A hook from
react-redux
to access the Redux store’s state. - fetchEmployees: An asynchronous thunk action to fetch employees from an API.
- Link: A component from
react-router-dom
used for navigation within the application.
const dispatch = useDispatch();
const employees = useSelector(state => state.employees.employees);
Component Definition
The EmployeeList
component is defined as a functional component.
Using Hooks
- useDispatch: Returns a reference to the
dispatch
function from the Redux store. This function is used to dispatch actions. - useSelector: Takes a selector function as an argument and returns the selected state. In this case, it retrieves the
employees
array from the Redux store’s state.
useEffect(() => {
dispatch(fetchEmployees());
}, [dispatch]);
useEffect Hook
- useEffect: This hook runs when the component mounts. The empty dependency array
[]
means it runs only once. - dispatch(fetchEmployees()): Dispatches the
fetchEmployees
thunk action to fetch employee data from the API when the component mounts.
return (
<div className="employee-list">
<h2>Employees List</h2>
<Link to="/add" className="btn btn-primary">Add Employee</Link>
<table className="table">
<thead>
<tr>
<th>Employee First Name</th>
<th>Employee Last Name</th>
<th>Employee Email Id</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{employees.map(employee => (
<tr key={employee.id}>
<td>{employee.firstName}</td>
<td>{employee.lastName}</td>
<td>{employee.email}</td>
<td>
<Link to={`/update/${employee.id}`} className="btn btn-info">Update</Link>
<Link to={`/delete/${employee.id}`} className="btn btn-danger">Delete</Link>
<Link to={`/view/${employee.id}`} className="btn btn-success">View</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
JSX Template
- Container: The main content is wrapped in a
div
with the classemployee-list
. - Heading: Displays “Employees List”.
- Link to Add Employee: A
Link
component to navigate to the “Add Employee” page. - Table: Displays the list of employees in a table format.
- Table Header: Defines the columns for first name, last name, email, and actions.
- Table Body: Maps over the
employees
array and renders a row for each employee.- Table Row: Each row displays the employee’s first name, last name, and email.
- Action Links: Links for updating, deleting, and viewing details of an employee.
Summary
Imports:
- Essential libraries and hooks (
React
,useEffect
,useDispatch
,useSelector
,Link
). fetchEmployees
thunk action from theemployeeSlice
.
- Essential libraries and hooks (
Component Definition:
- useDispatch: To dispatch actions.
- useSelector: To access the
employees
state from the Redux store.
useEffect:
- Dispatches the
fetchEmployees
action when the component mounts to fetch employee data from the API.
- Dispatches the
Rendering:
- Displays a list of employees in a table with action links for each employee (Update, Delete, View).
- Includes a link to the “Add Employee” page.
src/components/AddEmployee.js:
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { addEmployee } from '../features/employees/employeeSlice';
const AddEmployee = () => {
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
const dispatch = useDispatch();
const navigate = useNavigate();
const handleChange = (e) => {
const { name, value } = e.target;
setEmployee({ ...employee, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addEmployee(employee));
navigate('/');
};
return (
<div className="add-employee">
<h2>Add Employee</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" name="firstName" value={employee.firstName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value={employee.lastName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" name="email" value={employee.email} onChange={handleChange} required />
</div>
<button type="submit" className="btn btn-primary">Add</button>
</form>
</div>
);
};
export default AddEmployee;
Let’s break down the AddEmployee
component step-by-step to understand its functionality and how it works within a React and Redux application.
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { addEmployee } from '../features/employees/employeeSlice';
Import Statements
- React: The core React library for building user interfaces.
- useState: A React hook to manage the component’s state.
- useDispatch: A hook from
react-redux
to dispatch actions to the Redux store. - useNavigate: A hook from
react-router-dom
to navigate programmatically. - addEmployee: The action creator thunk to add a new employee, imported from the employee slice.
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
const dispatch = useDispatch();
const navigate = useNavigate();
Component Definition
The AddEmployee
component is defined as a functional component.
State and Hooks
employee
: State to hold the new employee’s data (first name, last name, email).setEmployee
: Function to update theemployee
state.dispatch
: Function to dispatch actions to the Redux store.navigate
: Function to navigate to different routes programmatically.
const handleChange = (e) => {
const { name, value } = e.target;
setEmployee({ ...employee, [name]: value });
};
Handling Input Changes
- handleChange: Updates the
employee
state whenever an input field changes. The input field’sname
attribute is used to determine which field to update.
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addEmployee(employee));
navigate('/');
};
Handling Form Submission
- handleSubmit: Prevents the default form submission behavior, dispatches the
addEmployee
action with the currentemployee
state, and navigates back to the home page (/
) upon successful submission.
return (
<div className="add-employee">
<h2>Add Employee</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" name="firstName" value={employee.firstName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value={employee.lastName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" name="email" value={employee.email} onChange={handleChange} required />
</div>
<button type="submit" className="btn btn-primary">Add</button>
</form>
</div>
);
JSX Template
- Container: The main content is wrapped in a
div
with the classadd-employee
. - Heading: Displays “Add Employee”.
- Form: A form to capture the new employee’s details.
- Form Groups: Each group contains a label and an input field for first name, last name, and email.
- Input Fields: Controlled components that update the
employee
state on change. - Submit Button: A button to submit the form and add the new employee.
src/components/UpdateEmployee.js:
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { updateEmployee } from '../features/employees/employeeSlice';
const UpdateEmployee = () => {
const { id } = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
const employees = useSelector(state => state.employees.employees);
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
useEffect(() => {
const currentEmployee = employees.find(emp => emp.id === parseInt(id));
if (currentEmployee) {
setEmployee(currentEmployee);
}
}, [id, employees]);
const handleChange = (e) => {
const { name, value } = e.target;
setEmployee({ ...employee, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
dispatch(updateEmployee({ id, updatedEmployee: employee }));
navigate('/');
};
return (
<div className="update-employee">
<h2>Update Employee</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" name="firstName" value={employee.firstName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value={employee.lastName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" name="email" value={employee.email} onChange={handleChange} required />
</div>
<button type="submit" className="btn btn-primary">Update</button>
</form>
</div>
);
};
export default UpdateEmployee;
Let’s break down the UpdateEmployee
component step-by-step to understand its functionality and how it integrates with React and Redux.
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { updateEmployee } from '../features/employees/employeeSlice';
Import Statements
- React: The core React library for building user interfaces.
- useState: A React hook to manage the component’s local state.
- useEffect: A React hook to handle side effects in functional components.
- useDispatch: A hook from
react-redux
to dispatch actions to the Redux store. - useSelector: A hook from
react-redux
to access the Redux store’s state. - useParams: A hook from
react-router-dom
to access URL parameters. - useNavigate: A hook from
react-router-dom
to programmatically navigate between routes. - updateEmployee: The action creator thunk to update an existing employee, imported from the employee slice.
const { id } = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
const employees = useSelector(state => state.employees.employees);
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
Component Definition
The UpdateEmployee
component is defined as a functional component.
State and Hooks
id
: Extracted from the URL usinguseParams
. This is the ID of the employee to be updated.dispatch
: A function to dispatch actions to the Redux store.navigate
: A function to navigate programmatically.employees
: Accesses theemployees
slice from the Redux store.employee
: Local state to hold the current employee’s data being edited.setEmployee
: Function to update theemployee
state.
useEffect(() => {
const currentEmployee = employees.find(emp => emp.id === parseInt(id));
if (currentEmployee) {
setEmployee(currentEmployee);
}
}, [id, employees]);
useEffect Hook
- useEffect: Runs when the component mounts or when the
id
oremployees
changes. It finds the employee with the matchingid
from theemployees
array and sets theemployee
state with this employee’s data. employees.find(emp => emp.id === parseInt(id))
: Searches for the employee with the givenid
.setEmployee(currentEmployee)
: Updates the local state with the current employee’s data.
const handleChange = (e) => {
const { name, value } = e.target;
setEmployee({ ...employee, [name]: value });
};
Handling Input Changes
- handleChange: Updates the
employee
state whenever an input field changes. The input field’sname
attribute is used to determine which field to update.
const handleSubmit = (e) => {
e.preventDefault();
dispatch(updateEmployee({ id, updatedEmployee: employee }));
navigate('/');
};
Handling Form Submission
- handleSubmit: Prevents the default form submission behavior, dispatches the
updateEmployee
action with the currentemployee
state, and navigates back to the home page (/
) upon successful submission..
return (
<div className="update-employee">
<h2>Update Employee</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" name="firstName" value={employee.firstName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value={employee.lastName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" name="email" value={employee.email} onChange={handleChange} required />
</div>
<button type="submit" className="btn btn-primary">Update</button>
</form>
</div>
);
JSX Template
- Container: The main content is wrapped in a
div
with the classupdate-employee
. - Heading: Displays “Update Employee”.
- Form: A form to capture the updated employee’s details.
- Form Groups: Each group contains a label and an input field for first name, last name, and email.
- Input Fields: Controlled components that update the
employee
state on change. - Submit Button: A button to submit the form and update the employee.
Summary
Imports:
- Essential libraries and hooks (
React
,useState
,useEffect
,useDispatch
,useSelector
,useParams
,useNavigate
). updateEmployee
thunk action from theemployeeSlice
.
- Essential libraries and hooks (
Component Definition:
- useParams: Extracts the employee ID from the URL.
- useDispatch: Dispatches actions to the Redux store.
- useNavigate: Navigates programmatically after updating an employee.
- useSelector: Accesses the
employees
state from the Redux store. - useState: Manages the form state.
useEffect:
- Sets the local
employee
state when the component mounts or whenid
oremployees
changes.
- Sets the local
Event Handlers:
- handleChange: Updates the form state on input changes.
- handleSubmit: Dispatches the
updateEmployee
action and navigates back to the home page upon form submission.
Rendering:
- Displays a form with fields for first name, last name, and email, and a submit button to update the employee.
src/components/ViewEmployee.js:
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useSelector } from 'react-redux';
import axios from 'axios';
const ViewEmployee = () => {
const { id } = useParams();
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
const employees = useSelector(state => state.employees.employees);
useEffect(() => {
const currentEmployee = employees.find(emp => emp.id === parseInt(id));
if (currentEmployee) {
setEmployee(currentEmployee);
} else {
axios.get(`http://localhost:5000/employees/${id}`)
.then(response => {
setEmployee(response.data);
})
.catch(error => console.log(error));
}
}, [id, employees]);
return (
<div className="view-employee">
<h2>View Employee</h2>
<p>First Name: {employee.firstName}</p>
<p>Last Name: {employee.lastName}</p>
<p>Email: {employee.email}</p>
</div>
);
};
export default ViewEmployee;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
src/components/DeleteEmployee.js:
src/components/DeleteEmployee.js:
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { deleteEmployee } from '../features/employees/employeeSlice';
const DeleteEmployee = () => {
const { id } = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
useEffect(() => {
dispatch(deleteEmployee(id));
navigate('/');
}, [id, dispatch, navigate]);
return (
<div className="delete-employee">
<h2>Deleting Employee...</h2>
</div>
);
};
export default DeleteEmployee;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
src/components/Navbar.js:
import React from 'react';
import { Link } from 'react-router-dom';
const Navbar = () => {
return (
<nav className="navbar">
<h1>Employee Management App</h1>
<div className="links">
<Link to="/">Home</Link>
<Link to="/add">Add Employee</Link>
</div>
</nav>
);
};
export default Navbar;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Step 5: Creating the Main App Component
Update the main App.js
file to use the Redux Provider and include the necessary routes.
src/App.js:
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { Provider } from 'react-redux';
import Navbar from './components/Navbar';
import EmployeeList from './components/EmployeeList';
import AddEmployee from './components/AddEmployee';
import UpdateEmployee from './components/UpdateEmployee';
import ViewEmployee from './components/ViewEmployee';
import DeleteEmployee from './components/DeleteEmployee';
import store from './app/store';
import './App.css';
function App() {
return (
<Provider store={store}>
<Router>
<div className="App">
<Navbar />
<div className="content">
<Routes>
<Route exact path="/" element={<EmployeeList />} />
<Route path="/add" element={<AddEmployee />} />
<Route path="/update/:id" element={<UpdateEmployee />} />
<Route path="/view/:id" element={<ViewEmployee />} />
<Route path="/delete/:id" element={<DeleteEmployee />} />
</Routes>
</div>
</div>
</Router>
</Provider>
);
}
export default App;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Step 6: Styling the App
Ensure the CSS in App.css
matches the design provided in the image.
src/App.css:
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.navbar {
background-color: #333;
color: #fff;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.navbar h1 {
margin: 0;
}
.navbar .links a {
color: #fff;
margin-left: 20px;
text-decoration: none;
}
.btn {
padding: 5px 10px;
margin: 0 5px;
border: none;
color: #fff;
cursor: pointer;
}
.btn-primary {
background-color: #007bff;
}
.btn-info {
background-color: #17a2b8;
}
.btn-danger {
background-color: #dc3545;
}
.btn-success {
background-color: #28a745;
}
.table {
width: 100%;
margin: 20px 0;
border-collapse: collapse;
}
.table th, .table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.table th {
background-color: #f8f8f8;
}
.form-group {
margin: 10px 0;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
// Async thunks for API calls
export const fetchEmployees = createAsyncThunk('employees/fetchEmployees', async () => {
const response = await axios.get('http://localhost:5000/employees');
return response.data;
});
export const addEmployee = createAsyncThunk('employees/addEmployee', async (newEmployee) => {
const response = await axios.post('http://localhost:5000/employees', newEmployee);
return response.data;
});
export const updateEmployee = createAsyncThunk('employees/updateEmployee', async ({ id, updatedEmployee }) => {
const response = await axios.put(`http://localhost:5000/employees/${id}`, updatedEmployee);
return response.data;
});
export const deleteEmployee = createAsyncThunk('employees/deleteEmployee', async (id) => {
await axios.delete(`http://localhost:5000/employees/${id}`);
return id;
});
const employeeSlice = createSlice({
name: 'employees',
initialState: {
employees: [],
status: null,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchEmployees.fulfilled, (state, action) => {
state.employees = action.payload;
})
.addCase(addEmployee.fulfilled, (state, action) => {
state.employees.push(action.payload);
})
.addCase(updateEmployee.fulfilled, (state, action) => {
const index = state.employees.findIndex(emp => emp.id === action.payload.id);
state.employees[index] = action.payload;
})
.addCase(deleteEmployee.fulfilled, (state, action) => {
state.employees = state.employees.filter(emp => emp.id !== action.payload);
});
},
});
export default employeeSlice.reducer;
Let’s break down this code step by step to understand how it sets up the Redux slice and handles asynchronous operations with Redux Toolkit.
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import axios from 'axios';
Import Statements
createSlice
: A function from Redux Toolkit that simplifies creating Redux slices (a slice of the state with its actions and reducers).createAsyncThunk
: A function from Redux Toolkit for handling asynchronous logic and automatically generating pending, fulfilled, and rejected action types.axios
: A promise-based HTTP client used to make API requests.
Step 4: Creating Components
Create the necessary component files and use the Redux store to manage state.
src/components/EmployeeList.js:
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchEmployees } from '../features/employees/employeeSlice';
import { Link } from 'react-router-dom';
const EmployeeList = () => {
const dispatch = useDispatch();
const employees = useSelector(state => state.employees.employees);
useEffect(() => {
dispatch(fetchEmployees());
}, [dispatch]);
return (
<div className="employee-list">
<h2>Employees List</h2>
<Link to="/add" className="btn btn-primary">Add Employee</Link>
<table className="table">
<thead>
<tr>
<th>Employee First Name</th>
<th>Employee Last Name</th>
<th>Employee Email Id</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{employees.map(employee => (
<tr key={employee.id}>
<td>{employee.firstName}</td>
<td>{employee.lastName}</td>
<td>{employee.email}</td>
<td>
<Link to={`/update/${employee.id}`} className="btn btn-info">Update</Link>
<Link to={`/delete/${employee.id}`} className="btn btn-danger">Delete</Link>
<Link to={`/view/${employee.id}`} className="btn btn-success">View</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
};
export default EmployeeList;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
src/components/AddEmployee.js:
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { addEmployee } from '../features/employees/employeeSlice';
const AddEmployee = () => {
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
const dispatch = useDispatch();
const navigate = useNavigate();
const handleChange = (e) => {
const { name, value } = e.target;
setEmployee({ ...employee, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
dispatch(addEmployee(employee));
navigate('/');
};
return (
<div className="add-employee">
<h2>Add Employee</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" name="firstName" value={employee.firstName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value={employee.lastName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" name="email" value={employee.email} onChange={handleChange} required />
</div>
<button type="submit" className="btn btn-primary">Add</button>
</form>
</div>
);
};
export default AddEmployee;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
src/components/UpdateEmployee.js:
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { updateEmployee } from '../features/employees/employeeSlice';
const UpdateEmployee = () => {
const { id } = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
const employees = useSelector(state => state.employees.employees);
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
useEffect(() => {
const currentEmployee = employees.find(emp => emp.id === parseInt(id));
if (currentEmployee) {
setEmployee(currentEmployee);
}
}, [id, employees]);
const handleChange = (e) => {
const { name, value } = e.target;
setEmployee({ ...employee, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
dispatch(updateEmployee({ id, updatedEmployee: employee }));
navigate('/');
};
return (
<div className="update-employee">
<h2>Update Employee</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" name="firstName" value={employee.firstName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" name="lastName" value={employee.lastName} onChange={handleChange} required />
</div>
<div className="form-group">
<label>Email</label>
<input type="email" name="email" value={employee.email} onChange={handleChange} required />
</div>
<button type="submit" className="btn btn-primary">Update</button>
</form>
</div>
);
};
export default UpdateEmployee;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
src/components/ViewEmployee.js:
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useSelector } from 'react-redux';
import axios from 'axios';
const ViewEmployee = () => {
const { id } = useParams();
const [employee, setEmployee] = useState({ firstName: '', lastName: '', email: '' });
const employees = useSelector(state => state.employees.employees);
useEffect(() => {
const currentEmployee = employees.find(emp => emp.id === parseInt(id));
if (currentEmployee) {
setEmployee(currentEmployee);
} else {
axios.get(`http://localhost:5000/employees/${id}`)
.then(response => {
setEmployee(response.data);
})
.catch(error => console.log(error));
}
}, [id, employees]);
return (
<div className="view-employee">
<h2>View Employee</h2>
<p>First Name: {employee.firstName}</p>
<p>Last Name: {employee.lastName}</p>
<p>Email: {employee.email}</p>
</div>
);
};
export default ViewEmployee;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
src/components/DeleteEmployee.js:
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { deleteEmployee } from '../features/employees/employeeSlice';
const DeleteEmployee = () => {
const { id } = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
useEffect(() => {
dispatch(deleteEmployee(id));
navigate('/');
}, [id, dispatch, navigate]);
return (
<div className="delete-employee">
<h2>Deleting Employee...</h2>
</div>
);
};
export default DeleteEmployee;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
src/components/Navbar.js:
import React from 'react';
import { Link } from 'react-router-dom';
const Navbar = () => {
return (
<nav className="navbar">
<h1>Employee Management App</h1>
<div className="links">
<Link to="/">Home</Link>
<Link to="/add">Add Employee</Link>
</div>
</nav>
);
};
export default Navbar;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Step 5: Creating the Main App Component
Update the main App.js
file to use the Redux Provider and include the necessary routes.
src/App.js:
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { Provider } from 'react-redux';
import Navbar from './components/Navbar';
import EmployeeList from './components/EmployeeList';
import AddEmployee from './components/AddEmployee';
import UpdateEmployee from './components/UpdateEmployee';
import ViewEmployee from './components/ViewEmployee';
import DeleteEmployee from './components/DeleteEmployee';
import store from './app/store';
import './App.css';
function App() {
return (
<Provider store={store}>
<Router>
<div className="App">
<Navbar />
<div className="content">
<Routes>
<Route exact path="/" element={<EmployeeList />} />
<Route path="/add" element={<AddEmployee />} />
<Route path="/update/:id" element={<UpdateEmployee />} />
<Route path="/view/:id" element={<ViewEmployee />} />
<Route path="/delete/:id" element={<DeleteEmployee />} />
</Routes>
</div>
</div>
</Router>
</Provider>
);
}
export default App;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
Step 6: Styling the App
Ensure the CSS in App.css
matches the design provided in the image.
src/App.css:
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.navbar {
background-color: #333;
color: #fff;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.navbar h1 {
margin: 0;
}
.navbar .links a {
color: #fff;
margin-left: 20px;
text-decoration: none;
}
.btn {
padding: 5px 10px;
margin: 0 5px;
border: none;
color: #fff;
cursor: pointer;
}
.btn-primary {
background-color: #007bff;
}
.btn-info {
background-color: #17a2b8;
}
.btn-danger {
background-color: #dc3545;
}
.btn-success {
background-color: #28a745;
}
.table {
width: 100%;
margin: 20px 0;
border-collapse: collapse;
}
.table th, .table td {
padding: 10px;
border: 1px solid #ddd;
text-align: left;
}
.table th {
background-color: #f8f8f8;
}
.form-group {
margin: 10px 0;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
Welcome to DevTechTutor.com, your ultimate resource for mastering web development and technology! Whether you're a beginner eager to dive into coding or an experienced developer looking to sharpen your skills, DevTechTutor.com is here to guide you every step of the way. Our mission is to make learning web development accessible, engaging, and effective.