React Infinite render loop , when passing data as undefined when table is using useSortBy and usePagination Hooks.
rayanfer32 opened this issue · 9 comments
Describe the bug
I have reproduced the problem in this repl , please check it out.
https://stackblitz.com/edit/react-xutun7?file=src%2FApp.js,src%2FTable.js
Table.js
import React from 'react';
import { useTable, usePagination, useSortBy } from 'react-table';
export default function Table({ data = [], columns = [] }) {
// bug: when using useSortBy and usePagination hooks we cannot pass the data as undefined
// The sad part is i cannot handle the error inside the table component by using data || []
// After starring at my peice of code for 17hrs now i finally found the fix.
// it was the empty array initialization done in the parameters of Table component,
// which confused react somehow to go into infinte render loop
// To get rid of the render loop error just remove the empty array passed to the data parameter
// * this is a workaround, seems working bcz the data here has changed to [] somehow after couple of render tries
if (!data) {
console.log('Catching undefined data');
return;
}
let tableInstance = useTable(
{ data: data || [], columns },
useSortBy,
usePagination
);
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
App.js
import React from 'react';
import Table from './Table';
import './style.css';
export default function App() {
const columns = [
{
Header: 'Supply',
accessor: 'currentsupply',
},
{
Header: 'Max Supply',
accessor: 'maxsupply',
},
];
return (
<div>
<Table columns={columns} data={undefined} />
</div>
);
}
Error in /turbo_modules/react-dom@18.2.0/cjs/react-dom.development.js (27292:11)
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.
Your minimal, reproducible example
https://stackblitz.com/edit/react-xutun7?file=src%2FApp.js,src%2FTable.js
Steps to reproduce
https://stackblitz.com/edit/react-xutun7?file=src%2FApp.js,src%2FTable.js
Expected behavior
Should not cause render loop
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
Windows / Linux
react-table version
7.8.0
TypeScript version
4.6.2
Additional context
No response
Terms & Code of Conduct
- I agree to follow this project's Code of Conduct
- I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.
Removing the empty array passed to data solves the problem , but I don't understand why
Before
export default function Table({ data = [], columns = [] }) { ... }
After
export default function Table({ data, columns = [] }) { ... }
Removing the empty array passed to data solves the problem , but I don't understand why
Before
export default function Table({ data = [], columns = [] }) { ... }After
export default function Table({ data, columns = [] }) { ... }
Because with data = []
data
will never be undefined inside Table
it will always be an empty array.
The infinite render is caused because you are creating a new empty array on every render and [] !== []
.
You can fix it by having a constant empty array:
import React from 'react';
import { useTable, usePagination, useSortBy } from 'react-table';
const emptyArray = [];
export default function Table({ data, columns }) {
let tableInstance = useTable(
{ data: data || emptyArray, columns },
useSortBy,
usePagination
);
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}
Another thing, columns
also has the same problem in this example, it should be memoized.
Removing the empty array passed to data solves the problem , but I don't understand why
Beforeexport default function Table({ data = [], columns = [] }) { ... }After
export default function Table({ data, columns = [] }) { ... }Because with
data = []
data
will never be undefined insideTable
it will always be an empty array.The infinite render is caused because you are creating a new empty array on every render and
[] !== []
.You can fix it by having a constant empty array:
import React from 'react'; import { useTable, usePagination, useSortBy } from 'react-table'; const emptyArray = []; export default function Table({ data, columns }) { let tableInstance = useTable( { data: data || emptyArray, columns }, useSortBy, usePagination ); return <pre>{JSON.stringify(data, null, 2)}</pre>; }Another thing,
columns
also has the same problem in this example, it should be memoized.
Thanks that explains why my code was going haywire! Have a nice day 😄
The infinite render is caused because you are creating a new empty array on every render and [] !== [].
This is nonsense, passing a new object to a hook should absolutely not cause an infinite render
Take this simple hook as an example
const useTest(arg: []) => {
return 'test'
}
...
const test = useTest([])
...
The example code does not cause an infinite render
I've looked at the source code of this library and while I cannot pinpoint the exact problem it definitely looks suspect
^ A state update on every render 🤔
You can break react in general by creating a new object on every render and passing that to anything that does Object.is() equality comparisons from render to render, e.g. useEffect
I agree this is nonsense, but that’s just React and technically how most immutable systems handle change detection.
The expectation that Table is going to deep diff everything you pass it is even more absurd, as this would grind most tables to a halt just to determine on every render if it should recompute or not, let alone rerender.
Has anyone successfully resolved this problem? After reviewing all the comments suggesting that columns and data be encapsulated within useMemo
, I implemented this approach. However, my application still falls into an infinite loop, and intriguingly, this occurs even when the columns and data are initialized as empty arrays. Incorporating the useSortBy
hook further exacerbates the issue, leading to a crash.
Has anyone successfully resolved this problem? After reviewing all the comments suggesting that columns and data be encapsulated within
useMemo
, I implemented this approach. However, my application still falls into an infinite loop, and intriguingly, this occurs even when the columns and data are initialized as empty arrays. Incorporating theuseSortBy
hook further exacerbates the issue, leading to a crash.
The render loop was caused by the empty array []
initialization in the parameters, which makes react create a new array every single time it re-renders and as we know in javascript [] !== []
this render loop issue arises. Check if you are doing something similar thing with an object too.
Has anyone successfully resolved this problem? After reviewing all the comments suggesting that columns and data be encapsulated within
useMemo
, I implemented this approach. However, my application still falls into an infinite loop, and intriguingly, this occurs even when the columns and data are initialized as empty arrays. Incorporating theuseSortBy
hook further exacerbates the issue, leading to a crash.
Memoizing data, columns, declaring EMPTY_ARRAY outside the react component and then using it inside as a fallback worked for me:
const EMPTY_ARRAY = [];
const Component = ()=> {
...
const table = useTable({
data: memoizedData || EMPTY_ARRAY,
columns: memoizedColumns,
...
Removing the empty array passed to data solves the problem , but I don't understand why
Beforeexport default function Table({ data = [], columns = [] }) { ... }After
export default function Table({ data, columns = [] }) { ... }Because with
data = []
data
will never be undefined insideTable
it will always be an empty array.The infinite render is caused because you are creating a new empty array on every render and
[] !== []
.You can fix it by having a constant empty array:
import React from 'react'; import { useTable, usePagination, useSortBy } from 'react-table'; const emptyArray = []; export default function Table({ data, columns }) { let tableInstance = useTable( { data: data || emptyArray, columns }, useSortBy, usePagination ); return <pre>{JSON.stringify(data, null, 2)}</pre>; }Another thing,
columns
also has the same problem in this example, it should be memoized.
i try solve this problem 2 days. Thank you, man