In the course of trying to put together this ledger application, I wanted to have a nice looking table with drag-and-drop functionality. I figure React should be really good for this, with some Bootstrap styling, whammo you have a nice looking table.
After looking through some reviews and such, I decided to go with react-table since it seems to be the most widely used, and supports drag and drop. The only catch is it’s written with “Hooks”, this newfangled React thing. The branding on this is huge. Everything is going Hooks. So I wire up this react-table example in my Ledger component, and guess what! Hooks don’t work with class components. Everything is going Functional. This is starting to feel a little forced! Like they’re just trying to create a secret club that everyone now has to learn the passcode for.
I have to learn the passcode, though, if I am going to have a pretty table. So I make a functional react-table component, and now there’s an infinite loop! Have you seen this marvel?
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.
Okay, so the first thing I realize is that I’m setting state inside useEffect() like this:
useEffect(() => {
$.ajax({
type: 'get',
url: props.parent.getConfig().baseURL + 'ledger/'
}).done(function (ajaxData) {
setTransactions(ajaxData);
}).fail(function (jqXHR, textStatus, errorThrown) {
props.parent.showAlert('Server Error', 'Server returned a status of ' + jqXHR.status);
});
});
Did you see what I did wrong there? That’s right, the first little secret… the empty array as the second argument.
useEffect(() => {
$.ajax({
type: 'get',
url: props.parent.getConfig().baseURL + 'ledger/'
}).done(function (ajaxData) {
setTransactions(ajaxData);
}).fail(function (jqXHR, textStatus, errorThrown) {
props.parent.showAlert('Server Error', 'Server returned a status of ' + jqXHR.status);
});
}, []); // <-- that little sucker right there
Drove myself crazy trying to figure that one out, trying to stop the infinite loop with a flag in the state (nope) or load the data in the parent component (nope again). That infinite loop will find you.
So I’m all proud of myself for figuring this out, I fire up the app again and:
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.
What!!!! I just solved that problem!!! Well it turns out you have to do this in useMemo too, which react-table uses:
const data = React.useMemo(() => transactions, [transactions]); // <-- that guy there
const columns = React.useMemo(() => [
{ Header: 'Date', accessor: 'transdate' },
{ Header: 'From', accessor: 'sourcename' },
{ Header: 'To', accessor: 'destname' },
{ Header: 'Note', accessor: 'comments' },
{ Header: 'Amount', accessor: 'amount' },
{ Header: 'Balance', accessor: 'balance' },
{ Header: 'Reconciled', accessor: 'reconciled' }
], []); // <-- and this little sucker right here
Notice that I didn’t use an empty array for [transactions]
, since that actually changes. If I use an empty one there, the transactions never make it into the table.
I hope you’ve enjoyed this edition of what John learns in an evening of whacking his head against a brick wall. Maybe it has saved you some trouble.