In this tutorial, we will build a fully functional Search, Sort, Filter & Pagination feature in a React application using JSON Server. We will use Axios for fetching data, and the dummy data will include fields like name, email, phone, address, status, and id. We will also incorporate modern CSS design for all components.
Creating a feature-rich application involves implementing various functionalities like search, sort, filter, and pagination. These features enhance user experience and make data management easier. In this article, we’ll go through each step required to build these features in a React application, backed by a JSON Server. We’ll use Axios for data fetching and ensure our design is modern and user-friendly.
Prerequisites
- Basic understanding of React
- Node.js and npm installed
- Basic knowledge of CSS
Setting Up the Project
Initialize a React Project:
npx create-react-app search-sort-filter-pagination
cd search-sort-filter-pagination
Install Axios and JSON Server:
npm install axios json-server
Setup JSON Server: Create a db.json
file in the project root with the following dummy data:
{
"users": [
{ "id": 1, "name": "John Doe", "email": "john@example.com", "phone": "123-456-7890", "address": "123 Main St", "status": "Active" },
{ "id": 2, "name": "Jane Smith", "email": "jane@example.com", "phone": "987-654-3210", "address": "456 Elm St", "status": "Inactive" }
]
}
Run JSON Server:
npx json-server --watch db.json --port 5000
Creating the React Components
App Component
This component will serve as the main container for our application.
// src/App.js
import React, { useState, useEffect } from 'react'; // Import React, useState, and useEffect
import axios from 'axios'; // Import Axios for data fetching
import SearchBar from './components/SearchBar'; // Import SearchBar component
import UserTable from './components/UserTable'; // Import UserTable component
import Pagination from './components/Pagination'; // Import Pagination component
const App = () => {
const [users, setUsers] = useState([]); // State to hold all user data
const [filteredUsers, setFilteredUsers] = useState([]); // State to hold filtered user data
const [searchTerm, setSearchTerm] = useState(''); // State to hold search term
const [currentPage, setCurrentPage] = useState(1); // State to hold current page number
const [usersPerPage] = useState(10); // State to hold number of users per page
// Fetch data from JSON Server
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await axios.get('http://localhost:5000/users'); // Fetch users from JSON Server
setUsers(response.data); // Set users state with fetched data
setFilteredUsers(response.data); // Set filtered users state with fetched data
} catch (error) {
console.error('Error fetching data', error); // Log any error that occurs during data fetching
}
};
fetchUsers(); // Call fetchUsers function
}, []); // Empty dependency array ensures this useEffect runs once on mount
// Filter users based on search term
useEffect(() => {
const results = users.filter(user =>
user.name.toLowerCase().includes(searchTerm.toLowerCase()) || // Check if name includes search term
user.email.toLowerCase().includes(searchTerm.toLowerCase()) || // Check if email includes search term
user.phone.includes(searchTerm) || // Check if phone includes search term
user.address.toLowerCase().includes(searchTerm.toLowerCase()) // Check if address includes search term
);
setFilteredUsers(results); // Set filtered users state with results
}, [searchTerm, users]); // Dependency array to re-run effect when searchTerm or users change
// Get current users for pagination
const indexOfLastUser = currentPage * usersPerPage; // Calculate index of last user on current page
const indexOfFirstUser = indexOfLastUser - usersPerPage; // Calculate index of first user on current page
const currentUsers = filteredUsers.slice(indexOfFirstUser, indexOfLastUser); // Slice filtered users array to get current users
// Change page
const paginate = (pageNumber) => setCurrentPage(pageNumber); // Function to set current page
return (
<div className="App">
<h1>User Management</h1>
<SearchBar setSearchTerm={setSearchTerm} /> // Render SearchBar component and pass setSearchTerm function as prop
<UserTable users={currentUsers} /> // Render UserTable component and pass currentUsers as prop
<Pagination
usersPerPage={usersPerPage}
totalUsers={filteredUsers.length}
paginate={paginate}
currentPage={currentPage}
/> // Render Pagination component and pass necessary props
</div>
);
};
export default App; // Export App component
SearchBar Component
This component allows users to search for specific data.
// src/components/SearchBar.js
import React from 'react'; // Import React
const SearchBar = ({ setSearchTerm }) => {
return (
<input
type="text" // Set input type to text
placeholder="Search..." // Set placeholder text
onChange={(e) => setSearchTerm(e.target.value)} // Call setSearchTerm function with input value on change
/>
);
};
export default SearchBar; // Export SearchBar component
UserTable Component
This component displays the user data in a table format.
// src/components/UserTable.js
import React from 'react'; // Import React
import './UserTable.css'; // Importing the CSS for modern design
const UserTable = ({ users }) => {
return (
<table className="user-table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Address</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<tr key={user.id}>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.email}</td>
<td>{user.phone}</td>
<td>{user.address}</td>
<td>{user.status}</td>
</tr>
))}
</tbody>
</table>
);
};
export default UserTable; // Export UserTable component
Pagination Component
This component handles the pagination of the user data.
// src/components/Pagination.js
import React from 'react'; // Import React
import './Pagination.css'; // Importing the CSS for modern design
const Pagination = ({ usersPerPage, totalUsers, paginate, currentPage }) => {
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(totalUsers / usersPerPage); i++) {
pageNumbers.push(i); // Add page number to pageNumbers array
}
return (
<nav>
<ul className="pagination">
{pageNumbers.map(number => (
<li key={number} className={`page-item ${currentPage === number ? 'active' : ''}`}>
<a onClick={() => paginate(number)} href="#!" className="page-link">
{number}
</a>
</li>
))}
</ul>
</nav>
);
};
export default Pagination; // Export Pagination component
Modern CSS Design
UserTable CSS
/* src/components/UserTable.css */
.user-table {
width: 100%; /* Set table width to 100% */
border-collapse: collapse; /* Collapse table borders */
margin: 20px 0; /* Add margin */
font-size: 1em; /* Set font size */
min-width: 400px; /* Set minimum width */
border-radius: 5px 5px 0 0; /* Set border radius */
overflow: hidden; /* Hide overflow */
box-shadow: 0 0 20px rgba(0, 0, 0, 0.15); /* Add box shadow */
}
.user-table thead tr {
background-color: #009879; /* Set background color */
color: #ffffff; /* Set text color */
text-align: left; /* Align text to the left */
font-weight: bold; /* Set font weight to bold */
}
.user-table th,
.user-table td {
padding: 12px 15px; /* Add padding */
}
.user-table tbody tr {
border-bottom: 1px solid #dddddd; /* Add bottom border */
}
.user-table tbody tr:nth-of-type(even) {
background-color: #f3f3f3; /* Set background color for even rows */
}
.user-table tbody tr:last-of-type {
border-bottom: 2px solid #009879; /* Set bottom border for last row */
}
.user-table tbody tr.active-row {
font-weight: bold; /* Set font weight to bold */
color: #009879; /* Set text color */
}
Pagination CSS
/* src/components/Pagination.css */
.pagination {
display: flex; /* Set display to flex */
justify-content: center; /* Center align items */
padding: 10px 0; /* Add padding */
}
.pagination .page-item {
margin: 0 5px; /* Add margin */
}
.pagination .page-link {
padding: 8px 16px; /* Add padding */
border: 1px solid #ddd; /* Add border */
border-radius: 5px; /* Set border radius */
text-decoration: none; /* Remove text decoration */
color: #009879; /* Set text color */
cursor: pointer; /* Set cursor to pointer */
}
.pagination .page-item.active .page-link {
background-color: #009879; /* Set background color */
color: #ffffff; /* Set text color */
border: 1px solid #009879; /* Set border */
}
Error Handling and Form Validation
In the above example, we have handled errors during data fetching with a try-catch block in the fetchUsers
function. For a more robust application, you can add more error handling and form validation techniques.
// src/App.js
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await axios.get('http://localhost:5000/users'); // Fetch users from JSON Server
setUsers(response.data); // Set users state with fetched data
setFilteredUsers(response.data); // Set filtered users state with fetched data
} catch (error) {
// Show error message to the user
alert('Error fetching data: ' + error.message); // Display error message
}
};
fetchUsers(); // Call fetchUsers function
}, []); // Empty dependency array ensures this useEffect runs once on mount
Conclusion
In this article, we built a comprehensive Search, Sort, Filter & Pagination feature in a React application using JSON Server and Axios. We used modern CSS to design our components and added error handling to ensure robustness. By following this tutorial, you can enhance your React applications with these powerful features, providing a better user experience.
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.