react and react table: Problem with Maximum update depth exceeded - reactjs

I'm getting this error when rendering a react-table in React typescript
caught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
That is the code related to this:
import {useEffect, useMemo, useState} from "react";
import ManagementService from "../services/ManagementService";
import MasterSet from "../model/MasterSet";
import {Column, useTable} from "react-table";
const ShowMastersets: React.FC = () => {
const [mastersets, setMastersets] = useState<Array<MasterSet>>([]);
const [searchName, setSearchName] = useState("");
useEffect(() => {
retrieveMastersets();
}, []);
const columns: Array<Column<MasterSet>> = [
{
Header: "Masterset ID",
accessor: "mastersetId"
},
{
Header: "Masterset Name",
accessor: "mastersetName"
},
{
Header: "Key Name",
accessor: "keyName"
},
{
Header: "Key Start Time",
accessor: "keyStartTime"
}
];
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow
} = useTable({
columns,
data: mastersets
});
return (
<div className="list row">
<div className="col-md-12 list">
<table
className="table table-striped table-bordered"
{...getTableProps()}
>
<thead>
{headerGroups.map((headerGroup: any) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column: any) => (
<th {...column.getHeaderProps()}>
{column.render("Header")}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
</div>
);
};
export default ShowMastersets;
Does anybody see what I am doing wrong? I have removed the parts of the controller which are not needed.

I would offer 2 suggestions to deal with this error: 1) create a type for your data, e.g.
export type MasterSet = {
mastersetId: string;
...
}
Then you can memoize it as follows:
const columns: Column<MasterSet>[] = useMemo(() => [
...
],[masterSet]);
turn autoResetPage to false in your useTable instance, like this:
...
const tableInstance: TableInstance = useTable(
{ columns, data, useSortBy, useExpanded,
initialState: { pageSize: 10, }, autoResetPage: false },
...allHooks
);
...
The latter especially should sort out the problem.

Related

React-Bootstrap table with firebase realtime database creates new table for each entry

I'm a little stuck here.
Yesterday I finished coding this app to display entries from a form.
It worked fine and showed each entry as a separate row. Now I started the app this morning and suddenly each new entry is rendered as a new table?
I tried to keep the code as short as possible to avoid any confusion, but I just don't know how to get my beloved table back. I must've accidentally missed a line of code somewhere, but I've been looking at this for hours, and I can't figure it out.
Help would be much appreciated.
import { db } from "./firebase";
import { ref, onValue } from "firebase/database";
import { useState, useEffect } from "react";
import { Table } from "react-bootstrap";
import "bootstrap/dist/css/bootstrap.min.css";
function App() {
const [entires, setentires] = useState([]);
useEffect(() => {
onValue(ref(db), (snapshot) => {
setentires([]);
const data = snapshot.val();
if (data !== null) {
Object.values(data).map((date) =>
setentires((oldArray) => [...oldArray, date])
);
}
});
}, []);
return (
<div className="App">
{entires.map((entry) => (
<>
<Table responsive striped bordered hover size="sm">
<thead>
<tr>
<th className="tableHeading">Date</th>
<th className="tableHeading">FirstName</th>
<th className="tableHeading">LastName</th>
<th className="tableHeading">Email</th>
<th className="tableHeading">Mobile</th>
<th className="tableHeading">Signature</th>
</tr>
</thead>
<tbody>
<tr key={entry}>
<td>{entry.date}</td>
<td>{entry.firstName}</td>
<td>{entry.lastName}</td>
<td>{entry.email}</td>
<td>{entry.phone}</td>
<td>
<img
alt="Signature"
className="sigImage"
src={entry.Signature}
></img>
</td>
</tr>
</tbody>
</Table>
</>
))}
</div>
);
}
export default App;
entries.map() is returning a whole table for each entry in the array since you have the whole table JSX inside the map function.
What you could do is to have a variable what will iterate over your entries and only return individual table records, and just include those records in the return statement of the component.
Every time something changes in the database, you get a snapshot with the entire data at ref and not just the changes. So you should replace the entire entires [sic] with the value from the snapshot.
In code that'd be something like:
onValue(ref(db), (snapshot) => {
const data = snapshot.val();
if (data !== null) {
setentires(Object.values(data));
}
});
I ended up using a different form package (Material-Table) and managed to make it work.
I also found a way to render images within the form, so that's a big bonus.
As you can see I ended up taking Frank van Puffelen's answer to simplify my data query.
Thanks for all the kind help!
import "../App.css";
import { db } from "../firebase";
import React, { useState, useEffect } from "react";
import { ref, onValue } from "firebase/database";
import MaterialTable, { MTableToolbar } from "#material-table/core";
import { ExportCsv, ExportPdf } from "#material-table/exporters";
const DataTable = () => {
const [Entries, setEntries] = useState([]);
useEffect(() => {
onValue(ref(db), (snapshot) => {
const data = snapshot.val();
if (data !== null) {
setEntries(Object.values(data));
}
});
}, []);
const data = Entries;
const columns = [
{
field: "date",
title: "Date",
width: "200px",
},
{ field: "firstName", title: "First Name", width: "200px" },
{ field: "lastName", title: "Last Name", width: "200px" },
{ field: "email", title: "E-Mail", width: "300px" },
{
field: "Signature",
title: "Signature",
width: "150px",
filtering: false,
render: (rowData) => (
<img alt="Signature" src={rowData.Signature} style={{ width: 100 }} />
),
},
];
return (
<div className="App">
<MaterialTable
data={data}
columns={columns}
options={{
rowStyle: {
backgroundColor: "White",
},
headerStyle: {
backgroundColor: "lightgray",
color: "black",
},
showTitle: false,
search: false,
filtering: true,
exportMenu: [
{
label: "Export PDF",
exportFunc: (cols, datas) =>
ExportPdf(cols, datas, "myPdfFileName"),
},
{
label: "Export CSV",
exportFunc: (cols, datas) =>
ExportCsv(cols, datas, "myCsvFileName"),
},
],
}}
components={{
Toolbar: (props) => (
<>
<div className="download">
<MTableToolbar {...props} />
</div>
<table className="toolBar">
<thead>
<tr className="row">
<th className="entryNum">
Number of Entries : {props.data.length}
</th>
</tr>
</thead>
</table>
</>
),
}}
/>
</div>
);
};
export default DataTable;

React table header is not properly aligning with table body

I am building a table using react table package and tailwindcss. But My table header is not properly aligning with the table body. Please see screenshots.
Table Component Code:
import React from "react";
import { useTable } from "react-table";
const Table = ({ columns, data }) => {
const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
useTable({
columns,
data,
});
return (
<div className="table-fixed border-collapse">
<table {...getTableProps()} className="block text-center">
<thead className="">
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
className="bg-primary-dark text-white p-4 text-center"
{...column.getHeaderProps()}
>
{column.render("Header")}
</th>
))}
</tr>
))}
</thead>
<tbody
{...getTableBodyProps()}
className="block overflow-auto h-72 bg-primary-light text-primary-dark "
>
{rows.map((row, i) => {
prepareRow(row);
return (
<tr className="block" {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td className="p-4" {...cell.getCellProps()}>
{cell.render("Cell")}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
);
};
export default Table;
Table Component Container code:
import React, { useMemo } from "react";
import useData from "../../hooks/useData";
import Table from "./Table";
const TableSection = React.memo(({ query }) => {
const { data, runtime, error } = useData(query);
const column =
data.length > 0 &&
Object.keys(data[0]).map((key) => {
return {
Header: data[0][key],
accessor: key,
};
});
const columns = useMemo(() => column, [column]);
const queryData = useMemo(() => data.slice(1), [data]);
return (
<div className="col-start-2 col-end-3 row-start-3 row-end-4 text-white m-6">
<Table columns={columns} data={queryData} />
</div>
);
});
export default TableSection;

Problems with react-table using "useSortBy"

I am having some problems on react-table using useSortBy. With no useSortBy the table works fine.
Getting this error:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
var COLUMNS = [
{
Header: 'Data Criação',
accessor: 'createdAt',
Cell: ({value})=> {return value ? format(new Date(value), 'dd/MM/yyyy') : ''},
},
{
Header: 'Nome',
accessor: 'name'
},
{
Header: 'Telefone',
accessor: 'mobile'
},
{
Header: 'Email',
accessor: 'email'
},
{
Header: 'Action',
accessor: (hit)=>{
return <LeadTableAction item={hit} selection={handleLeadDataSelection}/>
}
}
]
const columns = useMemo(()=>COLUMNS, []);
const tableInst = useTable({
columns,
data:props.lead.leadData ? props.lead.leadData : [{}]
}, useSortBy);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow
} = tableInst;
On JSX:
<Table {...getTableProps()}>
<thead>
{headerGroups.map(hg=>{
return (
<tr {...hg.getHeaderGroupProps()}>
{hg.headers.map(h=>{
return (
<th {...h.getHeaderProps(h.getSortByToggleProps())}>
{h.render("Header")}
<span style={{marginLeft: '5px'}}>
{h.isSorted ? (h.isSortedDesc ? <i className="fas fa-sort-down"></i> : <i className="fas fa-sort-up"></i>) : ''}
</span>
</th>
)
})}
</tr>
)
})}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row=>{
prepareRow(row)
return(
<tr {...row.getRowProps()}>
{row.cells.map(cell=>{
return(
<td {...cell.getCellProps()}>
{cell.render('Cell')}
</td>
)
})}
</tr>
)
})}
</tbody>
Can anybody help ?
Problem solved,
I just add a memo in my code:
const data = useMemo(()=>{
return props.lead.leadData ? props.lead.leadData : [{}]
}, [props.lead.leadData]);
This props.lead is the data to fetch direct on the table.
Done! :)
The whole time I tried passing an empty dependency array and my code did not throw any error there. It showed me errors only where I used hooks. I passed the data in the dependency hook in the useMemo array and it worked.
const data = useMemo(() => (employees.data), [employees.data]);

Getting Information/Data/Values from a row in React-Table

I literally have stayed up all night trying to figure out how to grab a value from my row.
const columns = useMemo(
() => [
{
// first group - TV Show
Header: "Shop Chop Chop List",
// First group columns
columns: [
{
Header: "User",
accessor: "email",
},
{
Header: "Store",
accessor: "store",
},
],
},
{
Header: "Details",
columns: [
{
Header: "Item",
accessor: "title",
},
{
Header: "Picture",
accessor: "picture",
Cell: ({ row }) => (
<a
target="_blank"
rel="noopener noreferrer"
href={row.original.picture}
>
{row.original.picture}
</a>
),
},
{
Header: "Aisle",
accessor: "aisleLocation",
},
{
Header: "Location",
id: 'edit',
accessor: 'id',
Cell: ({value}) => (
<div>
<button
onClick={()=> {
console.log(value);
}}
className={styles.editBtn}
>
Record Aisle
</button>
</div>
),
},
{
Header: "Remove",
id: "delete",
accessor: (str) => "delete",
Cell: (row)=> (
<button
className={styles.deleteBtn}
onClick={()=> {
const dataCopy = [...data];
dataCopy.splice(row.index, 1);
setData(dataCopy);
}}>
Found
</button>
)
}
],
},
],
[data],
);
This is my latest attempt. I am trying to grab a value from this row so I can assign a aisle location on the back end. I would prefer the uniqueID, but I could make it work with title and store as well. It is so damn hard to grab the information out of this row.
Here is my tableContainer
import React, { Fragment } from 'react';
import {
useTable,
useSortBy,
useFilters,
useExpanded,
usePagination,
} from 'react-table';
import { Table, Row, Col, Button, Input} from 'reactstrap';
import { Filter, DefaultColumnFilter } from './Filters';
const TableContainer = ({ columns, data, renderRowSubComponent }) => {
const {
getTableProps,
getTableBodyProps,
headerGroups,
page,
prepareRow,
visibleColumns,
canPreviousPage,
canNextPage,
pageOptions,
pageCount,
gotoPage,
nextPage,
previousPage,
state: { pageIndex },
} = useTable(
{
columns,
data,
defaultColumn: { Filter: DefaultColumnFilter },
initialState: { pageIndex: 0, pageSize: 5 },
},
useFilters,
useSortBy,
useExpanded,
usePagination
);
const generateSortingIndicator = (column) => {
return column.isSorted ? (column.isSortedDesc ? ' 🔽' : ' 🔼') : '';
};
const onChangeInInput = (event) => {
const page = event.target.value ? Number(event.target.value) - 1 : 0;
gotoPage(page);
};
return (
<Fragment>
<Table bordered hover {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th {...column.getHeaderProps()}>
<div {...column.getSortByToggleProps()}>
{column.render('Header')}
{generateSortingIndicator(column)}
</div>
<Filter column={column} />
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row) => {
prepareRow(row);
return (
<Fragment key={row.getRowProps().key}>
<tr onClick={()=> handleShow(row.original)}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
);
})}
</tr>
{row.isExpanded && (
<tr>
<td colSpan={visibleColumns.length}>
{renderRowSubComponent(row)}
</td>
</tr>
)}
</Fragment>
);
})}
</tbody>
</Table>
Please, can anyone help? I am new so very specific instructions are desired
please, and thank you
react-table is meant for displaying the data, not the JSX element but you can solve the issue in two ways, first, you can pass a unique id in the row data and just like above use that in the Cell or you can just render the JSX element as a row data and use the unique id directly(for example some model Id which you are trying to delete) but rendering the JSX element as a data is a heavy task as memoization will fail and you will have to do provide extra logic to prevent rerendering.

How to delete item seleted in table product

I am trying to delete a product, but it's doesn't show success. I do not know how to get the id of that product to delete
My button onClick = {handleDelete} is import from component in other folder. I try to create handleDelete function, but I missing something in this case.
This is my code for that section
import React, { useState, useEffect } from "react";
import { Container, Row, Col, Table } from "react-bootstrap";
import Loading from "../../components/Loading";
import Button from "../../components/Button/index"
import firebaseApp from "../../api/config";
const ProductTableList = ({
products,
loading,
fetchProductRequest
}) => {
useEffect(() => {
fetchProductRequest();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const firebaseDb = firebaseApp.database();
const [currentId, setCurrentId] = useState("");
if (loading) {
return (
<Container>
<Row>
<Col>
<Loading />
</Col>
</Row>
</Container>
);
}
const handleDelete = (id) => {
const productId = firebaseDb.ref().push().key;
if (window.confirm("Are you sure to delete this record?")) {
firebaseDb
.ref("products")
.child(`products/${productId}`)
.remove((err) => {
if (err) console.log(err);
else setCurrentId("");
});
}
}
const handleUpdate = (event) => {
//TODO
}
return (
<Table striped bordered hover className="product-table">
<thead>
<tr>
<th>No.</th>
<th className="image">Image</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{!!products && products.length > 0 ? (
products.map((product, index) => {
return (
<tr key={index}>
<td>{index}</td>
<td>{product.image}</td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>{product.description}</td>
<td>
<Button onClick={handleDelete} btnText="Delete" />
<Button onClick={handleUpdate} btnText="Update" />
</td>
</tr>
);
})
) :
(
<tr><td className="center-title">Product list is empty!</td></tr>
)}
</tbody>
</Table>
)
}
export default ProductTableList;
Can anyone help me? How do I delete the product that I have selected
Can anyone explain or support for me why? Thank you so much
I made a example, you need to add your function on button click and use your item id to be removed.
import React, { useState, useEffect } from "react";
import { Table } from "react-bootstrap";
const ProductTableList = () => {
const [currentId, setCurrentId] = useState("");
const [products, setProducts] = useState([{
image: 'image',
name: '01',
category: '01',
price: '01',
description: '01'
},
{
image: 'image',
name: '02',
category: '02',
price: '02',
description: '02'
},
{
image: 'image',
name: '03',
category: '03',
price: '03',
description: '03'
}])
const handleDelete = (id) => {
const removeItem = products.filter((item) => item !== products[id])
setProducts(removeItem)
}
return (
<Table striped bordered hover className="product-table">
<thead>
<tr>
<th>No.</th>
<th className="image">Image</th>
<th>Name</th>
<th>Category</th>
<th>Price</th>
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{!!products && products.length > 0 ? (
products.map((product, index) => {
return (
<tr key={index}>
<td>{index}</td>
<td>{product.image}</td>
<td>{product.name}</td>
<td>{product.category}</td>
<td>{product.price}</td>
<td>{product.description}</td>
<td>
<button onClick={() => handleDelete(index)}>Delete</button>
</td>
</tr>
);
})
) :
(
<tr><td className="center-title">Product list is empty!</td></tr>
)}
</tbody>
</Table>
)
}
export default ProductTableList;
Also, avoid index as element key
{ items.map((item, index) => (<li key={index}>{item}</li>)) }
When a list item was added or removed, and the key kept the same, the React assumed that the DOM element had not changed, and the app could not render.
An alternative to cases that the list doesn't have a unique ID is to generate one using shortID.
https://www.npmjs.com/package/shortid

Resources