My code below runs but I get a "Too many re-renders" error. I do not know why. If I take out the dual switch and base it on a unit (like price) then it works. However, I can't find where my code causes the infinite loop. The internet says I may be using useState incorrectly. Any help is appreciated. I'm a newbie, so please understand that I might have done something stupid! Regardless, learning is great! Thanks.
import React from "react"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; import TableCell from "@mui/material/TableCell"; import TableContainer from "@mui/material/TableContainer"; import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; import Paper from "@mui/material/Paper"; import TableSortLabel from "@mui/material/TableSortLabel"; import { useState } from "react"; function createData(number, item, qty, price) { return { number, item, qty, price }; } const rows = [ createData(1, "Apple", 5, 3), createData(2, "Orange", 2, 2), createData(3, "Grapes", 3, 1), createData(4, "Tomato", 2, 1.6), createData(5, "Mango", 1.5, 4) ]; export default function SortedTable() { const [rowData, setRowData] = useState(rows); const [orderDirection, setOrderDirection] = useState("asc"); const [orderUnit, setOrderUnit] = useState("number"); const sortArray = (arr, orderBy, orderUn) => { switch (orderUn) { case "number": default: switch (orderBy) { case "asc": default: return arr.sort((a, b) => a.number > b.number ? 1 : b.number > a.number ? -1 : 0 ); case "desc": return arr.sort((a, b) => a.number > b.number ? 1 : b.number > a.number ? -1 : 0 ); } case "item": switch (orderBy) { case "asc": default: return arr.sort((a, b) => a.item > b.item ? 1 : b.item > a.item ? -1 : 0 ); case "desc": return arr.sort((a, b) => a.item < b.item ? 1 : b.item < a.item ? -1 : 0 ); } case "qty": switch (orderBy) { case "asc": default: return arr.sort((a, b) => a.qty > b.qty ? 1 : b.qty > a.qty ? -1 : 0 ); case "desc": return arr.sort((a, b) => a.qty < b.qty ? 1 : b.qty < a.qty ? -1 : 0 ); } case "price": switch (orderBy) { case "asc": default: return arr.sort((a, b) => a.price > b.price ? 1 : b.price > a.price ? -1 : 0 ); case "desc": return arr.sort((a, b) => a.price < b.price ? 1 : b.price < a.price ? -1 : 0 ); } } }; const handleSortRequest = (unit) => { setOrderUnit(orderUnit === unit); setRowData(sortArray(rows, orderDirection, orderUnit)); setOrderDirection(orderDirection === "asc" ? "desc" : "asc"); }; return ( <TableContainer component={Paper}> <Table aria-label="simple table" > <TableHead> <TableRow> <TableCell align="center" onClick={handleSortRequest("number")}><TableSortLabel active={true} direction={orderDirection}> S.No </TableSortLabel></TableCell> <TableCell align="center" onClick={handleSortRequest("item")}><TableSortLabel active={true} direction={orderDirection}> Item </TableSortLabel></TableCell> <TableCell align="center" onClick={handleSortRequest("qty")}><TableSortLabel active={true} direction={orderDirection}> Quantity (kg) </TableSortLabel></TableCell> <TableCell align="center" onClick={handleSortRequest("price")}> <TableSortLabel active={true} direction={orderDirection}> Price ($) </TableSortLabel> </TableCell> </TableRow> </TableHead> <TableBody> {rowData.map((row) => ( <TableRow key={row.number}> <TableCell width="100" component="td" scope="row" align="center"> {row.number} </TableCell> <TableCell align="center">{row.item}</TableCell> <TableCell align="center">{row.qty}</TableCell> <TableCell align="center">{row.price}</TableCell> </TableRow> ))} </TableBody> </Table> </TableContainer> ); }
I'm trying to make each column sortable based on the column clicked. Bonus question, why do my headers not match the columns below when rendered.
P粉8019040892023-09-15 11:54:02
You need to pass the function reference instead of calling the function directly. This way, the function will only be called when the event occurs, rather than immediately when the component renders.
onClick={() => handleSortRequest("item")}
I also refactored your sortArray function to make it readable. If it is wrong. Please don't disturb it. :D
const sortArray = useCallback((arr, orderBy, orderUn) => { const compare = (a, b, prop, order) => { if (order === "asc") { return a[prop] > b[prop] ? 1 : b[prop] > a[prop] ? -1 : 0; } else { return a[prop] < b[prop] ? 1 : b[prop] < a[prop] ? -1 : 0; } }; switch (orderUn) { case "number": default: return arr.sort((a, b) => compare(a, b, "number", orderBy)); case "item": return arr.sort((a, b) => compare(a, b, "item", orderBy)); case "qty": return arr.sort((a, b) => compare(a, b, "qty", orderBy)); case "price": return arr.sort((a, b) => compare(a, b, "price", orderBy)); } }, []);
Use useCallback to prevent unnecessary re-rendering when dependencies have not changed
const handleSortRequest = useCallback( (unit) => { setOrderUnit(orderUnit === unit); setRowData(sortArray(rows, orderDirection, orderUnit)); setOrderDirection(orderDirection === "asc" ? "desc" : "asc"); }, [orderDirection, orderUnit, sortArray] );
Hope these are helpful to you.