'Sort table by clicking on the column header
I'm new to ReactJS. I have created a table with 2 columns. Here is my code:
import React, { useState, useEffect } from 'react'
import { getUsers } from '../../services/userService'
import { useNavigate } from 'react-router-dom'
import Pagination from '@mui/material/Pagination'
const Table = () => {
const navigate = useNavigate()
const [users, setUsers] = useState([]);
const [currentUsers, setCurrentUsers] = useState([]);
const [search, setSearch] = useState('');
const [sorting, setSorting] = useState({ key: "name", ascending: true });
const pageItemCount = 15
const [pageCount, setPageCount] = useState(0)
const [currentPage, setCurrentPage] = useState(1)
useEffect(async () => {
try {
const response = await getUsers(search);
setUsers(response.data.users);
setPageCount(Math.ceil(response.data.users.length / pageItemCount))
setCurrentUsers(response.data.users.slice(0, pageItemCount))
} catch (error) { }
}, [search]);
/**************************************** */
useEffect(() => {
const currentUsersCopy = [...currentUsers];
const sortedCurrentUsers = currentUsersCopy.sort((a, b) => {
return a[sorting.key].localeCompare(b[sorting.key]);
});
setCurrentUsers(
sorting.ascending ? sortedCurrentUsers : sortedCurrentUsers.reverse()
);
}, [currentUsers, sorting]);
/*************************************** */
function applySorting(key, ascending) {
setSorting({ key: key, ascending: ascending });
}
/************************************** */
const changePage = (i) => {
setCurrentPage(i)
const startItem = ((i - 1) * pageItemCount) + 1
setCurrentUsers(users.slice(startItem - 1, (pageItemCount * i)))
}
const handleChange = (event, value) => {
changePage(value);
}
return (
<div dir='rtl' className='bg-background mt-10 px-5 rd1200:px-30 overflow-auto'>
<div className='flex flex-wrap justify-between items-center'>
<div>
<svg className='relative top-10 right-3' width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 5H20" stroke="#79899e" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
<path d="M14 8H17" stroke="#79899e" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
<path d="M21 11.5C21 16.75 16.75 21 11.5 21C6.25 21 2 16.75 2 11.5C2 6.25 6.25 2 11.5 2" stroke="#79899e" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
<path d="M22 22L20 20" stroke="#79899e" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" />
</svg>
<input type='text' className='my-3 py-2 pl-3 pr-10 text-sm text-text-secondary shadow-sm focus:ring-2 ring-text-secondary rounded-md w-full rd500:w-120' placeholder='search ..' onChange={(e) => setSearch(e.target.value)} value={search} />
</div>
<div className="flex justify-center">
<select className="form-select form-select-sm
my-3 py-2 pl-15 pr-2 text-sm text-text-secondary shadow-sm rounded-md w-full
focus:ring-2 focus:text-gray-700 focus:bg-white focus:border-blue-600 focus:outline-none " aria-label=".form-select-sm example">
<option selected value="1" onClick={() => navigate('/')}>users</option>
<option value="2" onClick={() => navigate('/ext')}>other users</option>
</select>
</div>
</div>
<table className='w-full border-separate rounded-md'>
<thead>
<tr className='bg-text-secondary text-white shadow-sm text-center'>
<th className='p-2' onClick={() => applySorting('name', !sorting.ascending)}>name</th>
<th className='p-2' onClick={() => applySorting('name', !sorting.ascending)}>phone</th>
</tr>
</thead>
<tbody>
{currentUsers.map((item, index) =>
<tr key={item.id} className={index % 2 === 0 ? 'bg-white shadow-sm text-center' : 'bg-text bg-opacity-5 shadow-sm text-center'}>
<td className='text-text text-sm p-2'>{item.name}</td>
<td className='text-text text-sm p-2'>{item.phone}</td>
</tr>
)}
</tbody>
</table>
<Pagination className="mt-2 pb-20" dir='ltr' page={currentPage} count={pageCount} onChange={handleChange} variant="outlined" shape="rounded" />
</div>
)
}
export default Table
I'm trying to sort this table by clicking on each column header. I tried to apply the solutions suggested in link1 and link2, but I have not been successful yet. How can I do this?
Edited: I edited my code and added the full code of my table.
Solution 1:[1]
I'm trying to sort this table by clicking on each column header.
One of the possible solution to achieve this type of functionality is to store current state of the sorting. To do this we can use useState hook
const [sorting, setSorting] = useState({ field: 'name', ascending: false })
As you can see, useState function accepts default value. In our case the default value will be the sorting default state. The field property tells which column to sort by, and ascending prop says in which order it should be sorted.
Next, we need to set event handlers to each header of the column.
<tr className='bg-text-secondary text-white shadow-sm text-center'>
<th className='p-2' onClick={() => applySorting('name', !sorting.ascending)}>name</th>
<th className='p-2' onClick={() => applySorting('phone', !sorting.ascending)}>phone</th>
</tr>
By clicking the column header we call the function applySorting which says what column should be sorted.
function applySorting(key, ascending) {
setSorting({ key: key, ascending: ascending });
}
The last thing thing we need to do is to sort your data stored in currentUsers variable.
This part of the code is tricky a bit, because it uses useEffect hook. You can read the React documentation to understand how it works.
Let's say this function will be called by React everytime you change the current state of your sorting variable.
useEffect(() => {
// Copy array to prevent data mutation
const currentUsersCopy = [...currentUsers];
// Apply sorting
const sortedCurrentUsers = currentUsersCopy.sort((a, b) => {
return a[sorting.key].localeCompare(b[sorting.key]);
});
// Replace currentUsers with sorted currentUsers
setCurrentUsers(
// Decide either currentUsers sorted by ascending or descending order
sorting.ascending ? sortedCurrentUsers : sortedCurrentUsers.reverse()
);
}, [currentUsers, sorting]);
I created this example on codesandbox where you can play around with this approach. Link to working example
I hope you enjoy learning React!
Solution 2:[2]
I would recommend:
- Use Amazon S3 Inventory, which can provide a daily or weekly CSV file listing all objects.
- Write a script to filter-out rows where the tag already exists, leaving only a list of objects that do not have the tag
- Use S3 Batch Operations to set the tag on those remaining objects
This is highly scalable.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Roman Mahotskyi |
| Solution 2 | John Rotenstein |
