displaying data in material ui table from api using fetch api - reactjs

I can't convert the api data into raw data on table. the data I can display on my console but don't know how to get it on the table.
here is my code for fetching dummy api data:-
const paginationTable = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((res) => {
// console.log(res);
setData(res);
});
}, []);
console.log(data);
// data.map((item) => {
// console.log(item.id);
// });
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const columns = [
{
title: "ID",
field: data.id,
editable: false,
headerStyle: {
color: "white",
backgroundColor: "hwb(12deg 0% 0% / 86%)",
},
},
{
title: "Station Name",
field: data.address.street,
headerStyle: {
color: "white",
backgroundColor: "hwb(12deg 0% 0% / 86%)",
},
},
{
title: "Location",
field: data.address.city,
headerStyle: {
color: "white",
backgroundColor: "hwb(12deg 0% 0% / 86%)",
},
},
{
title: "Status",
field: data.website,
headerStyle: {
color: "white",
backgroundColor: "hwb(12deg 0% 0% / 86%)",
},
},
];
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
return (
<>
{/* <APIdata/> */}
<Paper sx={{ width: "100%" }}>
<TableContainer sx={{ maxHeight: 440 }}>
<Table>
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell
key={column.title}
align={column.align}
style={{ top: 57, minWidth: column.minWidth }}
sx={{ fontSize: "1rem" }}
>
{column.title}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{data
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row, i) => {
return (
<TableRow hover role="checkbox" tabIndex={-1} key={i}>
{/* updated
<TableRow hover role="checkbox" tabIndex={-1} key={row.code}>
*/}
{columns.map((column) => {
const value = column.title;
// console.log(value);
return (
<>
<TableCell key={column} align={column.align}>
{column.format && typeof value === "number"
? column.format(value)
: value}
</TableCell>
</>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[5, 10, 25, 100]}
component="div"
count={data.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</Paper>
</>
);
};
export default paginationTable;
the data fetched from api is something like this:-
currently I'm getting a blank web page.If i replace field values with
field: data.address instead of data.address.city and data.address.street it gives me table with headings only.. like this
I tried using "material-table" and through that I was able to get the data on the table but I've to add a view button over there in each row so I'm using material-ui table for that so that I can use material-icons

Related

Unable to load the second page after API call in React Collapsible Table (Material UI)

Facing this issue where the React Collapsible Table (Material UI) does not seem to reflect with data from the API after I click the next page button on my react table.
When I load the page initially, it reflects with data from the first page of server side paginated data. (see below)
first-page
When I click on the next page, the table shows up blank.
second-page
Here is the weird bit. When I navigate to the third page, the height of the table area doubles and is still blank.
third-page
Here is the code. In a nutshell, I'm iterating through the column names and column values for the jobs and the runs and displaying that.
function Row(props) {
const { row, rows } = props;
// console.log(rows);
// console.log(Object.keys(row))
const [open, setOpen] = React.useState(false);
return (
<React.Fragment>
<TableRow sx={{ "& > *": { borderBottom: "unset" } }}>
<TableCell>
<IconButton
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
{Object.values(row).map((colVal, counter) => {
if (typeof colVal != "object") {
// console.log(counter);
// console.log(colVal);
if (counter == 0) {
return (
<TableCell key={counter} component="th" scope="row">
{colVal}
</TableCell>
);
} else {
return (
<TableCell
key={counter}
align="right"
style={{
whiteSpace: "normal",
wordWrap: "break-word",
maxWidth: "150px",
}}
>
{colVal}
</TableCell>
);
}
}
})}
</TableRow>
<TableRow
style={{
backgroundColor: "rgb(0, 127, 97,0.2)",
}}
>
<TableCell
style={{
paddingBottom: 0,
paddingTop: 0,
}}
colSpan={Object.keys(rows[0]).length}
>
<Collapse
in={open}
timeout="auto"
// unmountOnExit
>
<Box
sx={{
margin: 1,
maxHeight: 200,
overflow: "auto",
}}
>
<Typography variant="h6" gutterBottom component="div">
Runs
</Typography>
<Table size="small" aria-label="runs">
<TableHead>
<TableRow>
{Object.keys(rows[0].runs[0]).map(
(runColName, runColNameCounter) => {
if (runColNameCounter) {
// console.log(runColNameCounter);
// console.log(runColName);
if (runColNameCounter == 0) {
return (
<TableCell key={runColNameCounter}>
{runColName}
</TableCell>
);
} else {
return (
<TableCell
key={runColNameCounter}
style={{
whiteSpace: "normal",
wordWrap: "break-word",
}}
align="right"
>
{runColName}
</TableCell>
);
}
}
}
)}
{/* <TableCell>Date</TableCell> */}
{/* <TableCell>Run ID</TableCell> */}
{/* <TableCell align="right">Amount</TableCell> */}
{/* <TableCell align="right">Total price ($)</TableCell> */}
</TableRow>
</TableHead>
<TableBody>
{row.runs.map((runRow) => (
<TableRow
sx={{
borderTop: "2px solid #686868",
// borderBottom: "2px solid black",
}}
key={runRow.run_id}
>
{Object.values(runRow).map((runColVal, runCounter) => {
// console.log(runCounter);
// console.log(runColVal);
if (runCounter == 0) {
return (
<TableCell
key={runCounter}
component="th"
scope="row"
>
{runColVal}
</TableCell>
);
} else {
return (
<TableCell
key={runCounter}
align="right"
style={{
whiteSpace: "normal",
wordWrap: "break-word",
}}
>
{runColVal}
</TableCell>
);
}
})}
</TableRow>
))}
</TableBody>
</Table>
</Box>
</Collapse>
</TableCell>
</TableRow>
</React.Fragment>
);
}
// Row.propTypes = {
// row: PropTypes.shape({
// calories: PropTypes.number.isRequired,
// carbs: PropTypes.number.isRequired,
// fat: PropTypes.number.isRequired,
// history: PropTypes.arrayOf(
// PropTypes.shape({
// amount: PropTypes.number.isRequired,
// customerId: PropTypes.string.isRequired,
// date: PropTypes.string.isRequired,
// }),
// ).isRequired,
// name: PropTypes.string.isRequired,
// price: PropTypes.number.isRequired,
// protein: PropTypes.number.isRequired,
// }).isRequired,
// };
function TablePaginationActions(props) {
const theme = useTheme();
const { count, page, rowsPerPage, onPageChange } = props;
const handleFirstPageButtonClick = (event) => {
onPageChange(event, 0);
};
const handleBackButtonClick = (event) => {
onPageChange(event, page - 1);
};
const handleNextButtonClick = (event) => {
onPageChange(event, page + 1);
};
const handleLastPageButtonClick = (event) => {
onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1));
};
return (
<Box sx={{ flexShrink: 0, ml: 2.5 }}>
<IconButton
onClick={handleFirstPageButtonClick}
disabled={page === 0}
aria-label="first page"
>
{theme.direction === "rtl" ? <LastPageIcon /> : <FirstPageIcon />}
</IconButton>
<IconButton
onClick={handleBackButtonClick}
disabled={page === 0}
aria-label="previous page"
>
{theme.direction === "rtl" ? (
<KeyboardArrowRight />
) : (
<KeyboardArrowLeft />
)}
</IconButton>
<IconButton
onClick={handleNextButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
aria-label="next page"
>
{theme.direction === "rtl" ? (
<KeyboardArrowLeft />
) : (
<KeyboardArrowRight />
)}
</IconButton>
<IconButton
onClick={handleLastPageButtonClick}
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
aria-label="last page"
>
{theme.direction === "rtl" ? <FirstPageIcon /> : <LastPageIcon />}
</IconButton>
</Box>
);
}
TablePaginationActions.propTypes = {
count: PropTypes.number.isRequired,
onPageChange: PropTypes.func.isRequired,
page: PropTypes.number.isRequired,
rowsPerPage: PropTypes.number.isRequired,
};
export default function CollapsibleTable() {
let params = useParams();
let dispatch = useDispatch();
const [loading, setLoading] = React.useState(false);
// const allJobss = useSelector((state) => console.log(state));
const allJobsStatus = useSelector((state) => state.jobs.status);
const rows = useSelector((state) => state.jobs.jobs);
// const [success, setSuccess] = React.useState(false);
const kpiId = params.id;
const fromDateStr = window.sessionStorage.getItem("fromDate");
const toDateStr = window.sessionStorage.getItem("toDate");
var totalEntries = useSelector((state) => state.jobs.totalEntries);
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
// Avoid a layout jump when reaching the last page with empty rows.
const emptyRows =
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0;
const getJobsDispatch = (pageNum = 1) => {
if (!loading) {
// setSuccess(false);
console.log("loading");
setLoading(true);
dispatch(getJobs({ kpiId, fromDateStr, toDateStr, pageNum })).then(
(action) => {
try {
setLoading(false);
console.log("stop loading");
// setRows(action.payload.resp.allJobs);
} catch (e) {
console.log(e);
}
}
);
// console.log(allJobs);
// setRows(allJobs);
}
};
const handleChangePage = (event, newPage) => {
setPage(newPage);
var pageNum = newPage + 1;
getJobsDispatch(pageNum);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
useEffect(() => {
getJobsDispatch();
return () => {
// console.log(allJobs);
};
}, []);
return (
<TableContainer component={Paper}>
{loading && (
<CircularProgress
size={68}
sx={{
// color: green[500],
position: "absolute",
top: "50%",
left: "50%",
zIndex: 1,
}}
/>
)}
<Table aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell />
{Object.keys(rows[0]).map((colName, colNameCounter) => {
// console.log(rows);
if (typeof rows[0][colName] != "object") {
// console.log(colNameCounter);
// console.log(colName);
if (colNameCounter == 0) {
return <TableCell key={colNameCounter}>{colName}</TableCell>;
} else {
return (
<TableCell
key={colNameCounter}
style={{
whiteSpace: "normal",
wordWrap: "break-word",
maxWidth: "150px",
}}
align="right"
>
{colName}
</TableCell>
);
}
}
})}
</TableRow>
</TableHead>
<TableBody>
{(rowsPerPage > 0
? rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
: rows
).map((row) => (
<Row key={row.name} row={row} rows={rows} />
))}
{emptyRows > 0 && (
<TableRow style={{ height: 53 * emptyRows }}>
<TableCell colSpan={6} />
</TableRow>
)}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[]}
colSpan={3}
count={parseInt(totalEntries)}
rowsPerPage={5}
page={page}
SelectProps={{
inputProps: {
"aria-label": "rows per page",
},
native: true,
}}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</Table>
</TableContainer>
);
}

I am trying to filter out data but not able to display them properly

I am using MUI to display a dummy data from a json file onto the table.
I have an input field which onChange event would call the function to filter out data based on input and show on the table. The filter of the data works and is displayed on the table as the last row but the old data is also shown as well. I have tried different ways but still could not figure out what is the issue.
const [sdata, setsData] = useState(Data); // data from file
const [searched, setSearched] = useState(""); // maintain state of the search field
const [filteredResults, setFilterdResults] = useState([]) // maintain state of filtered data
// function to filter data...
const requestSearch = (value) => {
let filteredData = sdata.filter((val) => {
if (searched === '') {
return val
} else {
return val.student_name.toLowerCase().includes(value.toLowerCase())
}
})
setFilterdResults(filteredData)
console.log("Filtered Data", filteredData)
}
// search input filed **only one for now**
<div className="search">
<Box
sx={{ display: "flex", alignItems: "flex-end", paddingRight: "10px" }}
>
<SearchIcon sx={{ color: "action.active", mr: 1, my: 0.5 }} />
<TextField
id="student"
label="Student"
variant="standard"
value={searched}
onChange={(evt) => {
setSearched(evt.target.value)
requestSearch(evt.target.value);
// console.log(evt.target.value);
}}
/>
<SearchIcon sx={{ color: "action.active", mr: 1, my: 0.5 }} />
<TextField id="zone" label="Zone" variant="standard" />
</Box>
</div>
// displaying data based on condition (if there is a searched input or not)
<TableContainer>
<Table aria-label="studentInfo">
<TableHead>
<TableRow>
{columns.map((col) => (
<TableCell
sx={{
backgroundColor: "#662d91",
color: "white",
fontSize: matches ? "12" : "18px",
fontWeight: "bold",
}}
key={col.id}
align={col.align}
style={{
minWidth: matches ? col.minWidth : col.mobWidth,
paddingLeft: "5px",
}}
>
{col.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{searched.length = 1 ? (
<>
{
filteredResults.map((std) => (
<StyledTableRow
// style={{ backgroundColor: getZoneColor(std.zone) }}
key={std.tg}
>
<StyledTableCell>{std.tg}</StyledTableCell>
<StyledTableCell>{std.student_name}</StyledTableCell>
<StyledTableCell>{std.admin_no}</StyledTableCell>
<StyledTableCell>{std.action}</StyledTableCell>
<StyledTableCell
style={{
color:
// "black",
getZoneColor(std.zone),
}}
>
{std.zone}
</StyledTableCell>
</StyledTableRow>
))
}
</>
) : (
<>
{
sdata.map((std) => (
<StyledTableRow
// style={{ backgroundColor: getZoneColor(std.zone) }}
key={std.tg}
>
<StyledTableCell>{std.tg}</StyledTableCell>
<StyledTableCell>{std.student_name}</StyledTableCell>
<StyledTableCell>{std.admin_no}</StyledTableCell>
<StyledTableCell>{std.action}</StyledTableCell>
<StyledTableCell
style={{
color:
// "black",
getZoneColor(std.zone),
}}
>
{std.zone}
</StyledTableCell>
</StyledTableRow>
))
}
</>
)}
</TableBody>
</Table>
</TableContainer>
The weird thing is if I force reload the tab and do the search it runs flawlessly but the next time I search it causes the issue
After force reload first search
The next search
try code below
const filterData = (inputValue) => {
const filteredData = sdata.filter(i => i.student_name?.includes(inputValue))
setsetSearched(inputValue);
setFilterdResults(filteredData)
}
call the function on onChange property of your input
<input onChange={(e)=> filterData(e.target.value)} />
and change your condition to this
searched.length > 0

Cannot read properties of undefined (reading 'value-domain') in React.Js

I am having a problem that I don't know where it cause. Please help me out.
I am developing a stock management platform, it allows users to CRUD products.
When Users are on the product page, they will see a table with edit and delete buttons in every record.
Whenever they click on the delete button, it will show a PopOver to confirm the delete action.
And the error happens when I click "Yes" in the PopOver Component
I am using Material-UI version 4.12.3,
Here is my parent component:
const useStyles = makeStyles((theme) => ({
modal: {
display: "flex",
alignItems: "center",
justifyContent: "center",
},
paper: {
backgroundColor: theme.palette.background.paper,
border: "2px solid #000",
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
loading: {
display: "flex",
justifyContent: "center",
},
}));
const useRowStyles = makeStyles({
root: {
"& > *": {
borderBottom: "100%",
},
},
});
function createData(id, name, supplier, type, unit, quantity, cost, price, ingredients, action) {
return {
id,
name,
supplier,
type,
unit,
quantity,
cost,
price,
ingredients,
action,
};
}
function Row(props) {
const { row } = props;
const [open, setOpen] = React.useState(false);
const classes = useRowStyles();
const isCocktail = (type) => {
if (type === "Cocktail" || type === "Mocktail") {
return true;
} else {
return false;
}
};
return (
<React.Fragment>
<TableRow className={classes.root}>
<TableCell>
{isCocktail(row.type) ? (
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
) : null}
</TableCell>
<TableCell component="th" scope="row">
{row.id}
</TableCell>
<TableCell align="middle">{row.name}</TableCell>
<TableCell align="middle">{row.supplier}</TableCell>
<TableCell align="middle">{row.type}</TableCell>
<TableCell align="middle">{row.unit}</TableCell>
<TableCell align="middle">{row.quantity}</TableCell>
<TableCell align="middle">{row.cost}</TableCell>
<TableCell align="middle">{row.price}</TableCell>
<TableCell align="middle">{row.action}</TableCell>
</TableRow>
<TableRow></TableRow>
</React.Fragment>
);
}
function Ingredients({ controller }) {
const classes = useStyles();
const dispatch = useDispatch();
const loading = useSelector((state) => state.product.loading);
const listProducts = useSelector((state) => state?.product?.products?.products);
const role = useSelector((state) => state.auth.user.role);
const [open, setOpen] = React.useState(false);
const nonCocktailList = listProducts?.filter((e) => e.type !== "Cocktail");
const nonMocktailList = nonCocktailList?.filter((e) => e.type !== "Mocktail");
const nonFoodList = nonMocktailList?.filter((e) => e.type !== "Food");
const handleOpen = async (id) => {
await dispatch(productActions.getSingleProduct(id));
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
let vietNamD = Intl.NumberFormat("vi-VI");
let rows = nonFoodList?.map((product, index) => {
return createData(
index,
product?.name,
product?.supplier,
product?.unit,
`${product?.capacity + " " + product?.capacityUnit}`,
`${vietNamD.format(product?.cost)} đ`,
`${product?.quantity + " " + product?.unit}`,
<div className="actions">
<Button variant="contained" style={{ backgroundColor: "#2EC0FF", color: "white" }} onClick={() => handleOpen(product?._id)}>
<EditOutlinedIcon style={{ color: "white" }} />
</Button>
<PopOver id={product._id} role={role} controller={controller} />
</div>
);
});
useEffect(() => {
dispatch(supplierActions.getSuppliers());
}, [dispatch]);
return loading ? (
<div className={classes.loading}>
{" "}
<CircularProgress />
</div>
) : (
<div className="create-button">
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<CreateUpdateIngredient handleClose={handleClose} controller={controller} />
</Fade>
</Modal>
<IngredientModal controller={controller} />
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead style={{ backgroundColor: "black", width: "100%" }}>
<TableRow>
<TableCell />
<TableCell style={{ color: "white" }}>Index</TableCell>
<TableCell style={{ color: "white" }} align="middle">
Name
</TableCell>
<TableCell style={{ color: "white" }} align="middle">
Supplier
</TableCell>
<TableCell style={{ color: "white" }} align="middle">
Unit
</TableCell>
<TableCell style={{ color: "white" }} align="middle">
Unit Capacity
</TableCell>
<TableCell style={{ color: "white" }} align="middle">
Cost
</TableCell>
<TableCell style={{ color: "white" }} align="middle">
Daily Quantity Needed
</TableCell>
<TableCell
style={{
color: "white",
display: "flex",
justifyContent: "center",
}}
align="middle"
>
Action
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows?.map((row) => (
<Row key={row.id} row={row} />
))}
</TableBody>
</Table>
</TableContainer>
</div>
);
}
export default React.memo(Ingredients);
And this is the PopOver:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Popover from "#material-ui/core/Popover";
import Typography from "#material-ui/core/Typography";
import { Button, IconButton } from "#material-ui/core";
import DeleteOutlineOutlinedIcon from "#material-ui/icons/DeleteOutlineOutlined";
import { useDispatch } from "react-redux";
import { productActions } from "redux/actions";
const useStyles = makeStyles((theme) => ({
typography: {
padding: theme.spacing(2),
},
}));
export default function PopOver(props) {
const { controller, id, role } = props;
const classes = useStyles();
const dispatch = useDispatch();
const [anchorEl, setAnchorEl] = React.useState(null);
const deleteProduct = (id) => {
if (!id) return;
dispatch(productActions.deleteProduct(id));
dispatch(productActions.getProducts(controller));
handleClose();
};
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const productId = open ? "simple-popover" : undefined;
return (
<div>
<Button aria-describedby={id} style={{ backgroundColor: "#FF4D4F", height: "40px", width: "20px" }} variant="contained" onClick={handleClick} disabled={role !== "Admin"}>
<IconButton aria-label="delete" color="secondary">
<DeleteOutlineOutlinedIcon style={{ color: "white" }} />
</IconButton>
</Button>
<Popover
id={productId}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center",
}}
transformOrigin={{
vertical: "top",
horizontal: "center",
}}
>
<Typography className={classes.typography}>
Do you want to delete this ingredient?
<Button
color="primary"
onClick={() => {
deleteProduct(id);
}}
>
Yes
</Button>
<Button color="secondary" onClick={handleClose}>
No
</Button>
</Typography>
</Popover>
</div>
);
}
These are the two actions that called when a record is deleted
const getProducts =
({ date, mode }) =>
async (dispatch) => {
const body = { date, mode };
dispatch({ type: types.GET_PRODUCTS_REQUEST, payload: null });
try {
const res = await api.post("/products", body);
dispatch({ type: types.GET_PRODUCTS_SUCCESS, payload: res.data.data });
} catch (error) {
toast.error("Get Product List Failed");
dispatch({ type: types.GET_PRODUCTS_FAILURE, payload: error });
}
};
const deleteProduct = (id) => async (dispatch) => {
dispatch({ type: types.DELETE_PRODUCT_REQUEST, payload: null });
try {
const res = await api.delete(`/products/${id}`);
dispatch({ type: types.DELETE_PRODUCT_SUCCESS, payload: res.data.data });
toast.success("Delete Product Success");
} catch (error) {
toast.error("Delete Product Fail");
dispatch({ type: types.DELETE_PRODUCT_FAILURE, payload: error });
}
};

How can i make Material UI table scroll horizontally and vertically with fixed header

**This is material UI code for the sticky header and vertical scroll I want this with horizontal scroll also. I tried to use overflowX:"auto and scroll on paper but it didn't work" I also tried to use other tables to complete this work but got confused. OverflowX on paper isn't working. **
For more information or reference please check https://material-ui.com/components/tables/
const columns = [
{ id: 'name', label: 'Name', minWidth: 170 },
{ id: 'code', label: 'ISO\u00a0Code', minWidth: 100 },
{
id: 'population',
label: 'Population',
minWidth: 170,
align: 'right',
format: (value) => value.toLocaleString('en-US'),
},
{
id: 'size',
label: 'Size\u00a0(km\u00b2)',
minWidth: 170,
align: 'right',
format: (value) => value.toLocaleString('en-US'),
},
{
id: 'density',
label: 'Density',
minWidth: 170,
align: 'right',
format: (value) => value.toFixed(2),
},
];
function createData(name, code, population, size) {
const density = population / size;
return { name, code, population, size, density };
}
const rows = [
createData('India', 'IN', 1324171354, 3287263),
createData('China', 'CN', 1403500365, 9596961),
createData('Italy', 'IT', 60483973, 301340),
createData('United States', 'US', 327167434, 9833520),
createData('Canada', 'CA', 37602103, 9984670),
createData('Australia', 'AU', 25475400, 7692024),
createData('Germany', 'DE', 83019200, 357578),
createData('Ireland', 'IE', 4857000, 70273),
createData('Mexico', 'MX', 126577691, 1972550),
createData('Japan', 'JP', 126317000, 377973),
createData('France', 'FR', 67022000, 640679),
createData('United Kingdom', 'GB', 67545757, 242495),
createData('Russia', 'RU', 146793744, 17098246),
createData('Nigeria', 'NG', 200962417, 923768),
createData('Brazil', 'BR', 210147125, 8515767),
];
const useStyles = makeStyles({
root: {
width: '100%',
},
container: {
maxHeight: 440,
},
});
export default function StickyHeadTable() {
const classes = useStyles();
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
return (
<Paper className={classes.root}>
<TableContainer className={classes.container}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell
key={column.id}
align={column.align}
style={{ minWidth: column.minWidth }}
>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row) => {
return (
<TableRow hover role="checkbox" tabIndex={-1} key={row.code}>
{columns.map((column) => {
const value = row[column.id];
return (
<TableCell key={column.id} align={column.align}>
{column.format && typeof value === 'number' ? column.format(value) : value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[10, 25, 100]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Paper>
);
}
After searching I found that putting maxWidth on the container will allow you to have a vertically and horizontally scrollable table.

React 'Error: Maximum update depth exceeded.' with functional component and hooks

I am getting the following error:
Error: Maximum update depth exceeded.
With this component:
const UsersPage: React.FunctionComponent<UsersPageProps> = ({
location,
history,
match,
}): JSX.Element => {
const dispatch = useDispatch();
const { search } = location;
const query = parseInt(location.search, 10);
const [currentPage, setCurrentPage] = useState<number>(
isNaN(query) || query < 0 ? 0 : query
);
const users = useSelector((state: any) => state.manageUsers.users.results);
const isLoading = useSelector((state: any) => state.manageUsers.usersLoaded);
const totalPages = useSelector(
(state: any) => state.manageUsers.users.totalPages
);
const handlePaginationChange = (
event: React.ChangeEvent<any>,
pageNumber: number
): void => {
history.push(`${match.path}?page=${pageNumber ?? 0}`);
setCurrentPage(pageNumber ?? 0);
};
const handleAddUserOnClick = () =>
dispatch(manageUsersSetNewUserAndNavigate());
const handleEditOnClick = (id) =>
dispatch(manageUsersSetActiveEditUserAndNavigate(id));
useEffect(() => {
dispatch(manageUsersLoadUsers(currentPage));
}, [currentPage]);
return (
<section className="users page">
{!isLoading && <SectionLoaderWithMessage message={"Loading users..."} />}
{isLoading && (
<React.Fragment>
<Toolbar disableGutters={true}>
<Title flex={1}>Manage Users</Title>
<Button
variant="contained"
color="secondary"
onClick={handleAddUserOnClick}
startIcon={<AddCircle />}
>
Add User
</Button>
</Toolbar>
<TableContainer component={Paper}>
<Table aria-label="table">
<TableHead>
<TableRow style={{ backgroundColor: "white" }}>
<TableCell style={{ fontWeight: "bold", minWidth: 150 }}>
Email
</TableCell>
<TableCell style={{ fontWeight: "bold", minWidth: 150 }}>
Name
</TableCell>
<TableCell style={{ fontWeight: "bold", minWidth: 150 }}>
Surname
</TableCell>
<TableCell style={{ fontWeight: "bold", minWidth: 150 }}>
Firm
</TableCell>
<TableCell style={{ fontWeight: "bold", minWidth: 150 }}>
Type
</TableCell>
<TableCell style={{ fontWeight: "bold", minWidth: 150 }}>
Enabled
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{users.map((user, index) => (
<TableRow
key={`${user.id}-${index}`}
onClick={() => handleEditOnClick(user.id)}
hover
>
<TableCell>{user.email}</TableCell>
<TableCell>{user.name}</TableCell>
<TableCell>{user.surname}</TableCell>
<TableCell>{user.firmName}</TableCell>
<TableCell>{user.type}</TableCell>
<TableCell>{`${user.enabled}`}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<ThemedTablePagination
totalPages={totalPages}
pageNumber={currentPage}
onChange={handlePaginationChange}
/>
</TableContainer>
</React.Fragment>
)}
</section>
);
};
export default UsersPage;
I do not see anywhere in the render that is updating the state to cause an infinite loop.
The function handlePaginationChange should be inside useEffect.
Currently each time setCurrentPage is called it re-render the component which in-turn calls the handlePaginationChange function again, hence calling setCurrentPage again and the cycle goes on and on.

Resources