I'm trying to make a sortable user table that can be sorted by all the table heads. I have been able to get it to sort ascending and descending, but only by the name column. I'm new to reactjs and have not been able to implement it in my code.
here is the table component:
import { formatDate } from "../../utils/formatDate";
import "./table.css";
function Table(props) {
const { headerData, bodyData, removeItem, sortData, ordIcon} = props;
return (
<div className="user-data">
<table className="user-table">
<thead>
<tr className="data-th">
{headerData.map((headerTable) => (
<th onClick={sortData} id={"header-" headerTable.toLowerCase()}>
{headerTable} <img src={ordIcon} id="arrow-img"></img>
</th>
))}
</tr>
</thead>
<tbody>
{bodyData.map((item) => (
<tr className="data-tr">
<td>{item.name}</td>
<td>{item.email}</td>
<td>{item.occupation}</td>
<td>{formatDate(item.birthday)}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
export default Table;
and here is the sorting code in the Home:
const sortData = () => {
if (order === "" || "asc") {
let sortUserName = [...newUserArr].sort((a, b) =>
a.name.localeCompare(b.name)
);
setOrder("dsc");
setNewUserArr(sortUserName);
setOrdIcon(arrowDown)
} if (order === "dsc") {
let sortUserName = [...newUserArr].sort((a, b) =>
b.name.localeCompare(a.name)
);
setOrder("asc");
setNewUserArr(sortUserName);
setOrdIcon(arrowUp)
}
};
Thank you
edit: headerData={headerUser}
const headerUser = ["Name", "Email", "Occupation", "Birthday"];
CodePudding user response:
Here's one generic approach,
In Home
component, add a variable storing the key and type of every column
const columnMap = {
Name: {key: 'name', type: 'string'},
Email: {key: 'email', type: 'string'},
Occupation: {key: 'occupation', type: 'string'},
Birthday: {key: 'birthday', type: 'date'}
};
And then in the Table
component while rendering the column headers, change the onClick so that it sends the columnHeader
to sortData
function
{headerData.map((headerTable) => (
<th onClick={() => sortData(headerTable)} id={"header-" headerTable.toLowerCase()}>
{headerTable} <img src={ordIcon} id="arrow-img"></img>
</th>
))}
And finally in the sortData
function,
const sortData = (columnHeader) => {
const {key, type} = columnMap[columnHeader];
const sorted = [...newUserArr].sort((a, b) => {
let sortValue = 0;
if (type === 'date') {
sortValue = new Date(a[key]) - new Date(b[key]);
} else {
sortValue = a[key].localeCompare(b[key]);
}
return order === 'dsc' ? -(sortValue) : sortValue;
});
setNewUserArr(sorted);
if (order === '' || 'asc') {
setOrder('dsc');
setOrdIcon(arrowDown);
} else {
setOrder('asc');
setOrdIcon(arrowUp);
}
};
We first find the key using which we need to sort newUserArr
, we also need type here because for dates we cannot use localeCompare
. Then we sort the array, if order is dsc
then we reverse the sort.
EDIT -
For sort icons, we could keep track of the current sort state of each column and then use it in Table
component
In Home
component
const [sortStatus, setSortStatus] = useState({});
And change sortData
to
const sortData = (columnHeader) => {
const {key, type} = columnMap[columnHeader];
const sorted = [...newUserArr].sort((a, b) => {
let sortValue = 0;
if (type === 'date') {
sortValue = new Date(a[key]) - new Date(b[key]);
} else {
sortValue = a[key].localeCompare(b[key]);
}
return order === 'dsc' ? -(sortValue) : sortValue;
});
setNewUserArr(sorted);
setSortStatus((prev) => ({
...prev,
[columnHeader]: order === 'dsc' ? 'asc' : 'dsc'
}))
};
In Table
component,
const getSortIcon = (columnHeader) => {
if (!sortStatus[columnHeader]) {
return "↕︎";
}
return sortStatus[columnHeader] === 'dsc' ? "↓" : "↑";
};
{headerData.map((headerTable) => (
<th onClick={sortData} id={"header-" headerTable.toLowerCase()}>
{headerTable}
{getSortIcon(headerTable)}
</th>
))}