table
table copied to clipboard
Table keeps re-rendering
Describe the bug
I created table and it just keeps re-rendering, not really sure why
Your minimal, reproducible example
https://codesandbox.io/s/mantine-playground-forked-pslviy?file=/src/App.tsx:0-3415
Steps to reproduce
- Run the codesandbox
- Click on one of the table headers
- The component keeps re-rendering
Expected behavior
Table shouldn't be re-rendered unless content/data was changed
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
macOS, happens in both safari and chrome
react-table version
v8.7.4
TypeScript version
v4.9.3
Additional context
Found out that if the data
property that I pass to useReactTable
is wrapped in useMemo
the problem resolves, but why?
const table = useReactTable({
data: useMemo(() => [], []), // stops the problem from happening
...
})
also if you comment out the <tbody>
jsx the problem stops
Terms & Code of Conduct
- [x] I agree to follow this project's Code of Conduct
- [X] 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.
It looks like that the data
property in useReactTable
has to have the same reference across renders, otherwise @tanstack/react-table
keeps re-rendering the component...
The problem is that my data is passed from react-query
useQuery
, so I don't really have a solution
Anyone has any idea what can be the problem here?
I also noticed this is my app. This only happens when i use Nextjs for example.
Also im just passing a empty array for data
and column
Here is a codesandbox with the problem: codesandbox with nextjs
Here is a codesandbox without nextjs and you see that the render issue does not occur: codesandbox without nextjs
I'm also facing the same issue.
My data is passed from useSWR
and it just keeps rendering while my data is undefined.
Edit: I solved mine by using useMemo
and returning an empty array if the data is still undefined.
Stuck with the same issue
Guys, you pass new instance of data with each render. data has to be stable. If you need empty array, at least declare it next to the component const emptyArray = []; and use that instead of inlined []. Because [] !== [], but emptyArray === emptyArray
Quoted from original answer
Anyone who runs into this issue: It's because your data does not have a stable reference. Memoize it or store it in state.
I ran into this because I was filtering my data inside my functional react component, which caused the list to change, causing the table to change, causing a re-render. Memoizing did not work for me, I had to move the filter logic outside of my component that contained the react table.
I am facing the same issue in RedwoodJS
Edit: Solved using useMemo on both columns
and data
variable
I had the same issue using useQuery
with my table. The problem was that I was setting data to a default []
when pulling from useQuery
:
const {data = []} = useQuery( ... )
The fix was to do this logic inside the play where I was memoizing my data:
const {data: theData} = useQuery( ... )
const data = React.useMemo(() => {
return Array.isArray(theData) ? theData : [];
}, [theFiles]);
const columns = React.useMemo<ColumnDef<File>[]>(
() => tableColumns.map(column => COLUMNS[column]),
[tableColumns]
);
In my case the problem was caused not by data
but by columns
. The DataTable
had input
s in its cells which had a draft state that I was tracking at the level of the component that was rendering the DataTable
. On each change of the input
the draft state would update and that would update the columns
(such that the new cells would contain new input
s with the correct draft value. This works fine in a straight HTML table assuming your keys are set up properly, but in DataTable
the columns
seem to need to be referentially equal or the whole table re-renders completely (not just reconciles the VDOM changes to the DOM) and the input
that is being edited loses focus (because it is replaced by a new input
with new text).
I solved this by moving the draft state into its own component used at the cell level. The component is returned by cell
in columns
and takes the table row's model but encapsulates the draft state completely within itself. When the editing is done (on input
blur
or when the Enter key is pressed) I go and update the backing data source which invalidates data
and re-renders the whole table clearing the input
s draft state because again at that point the input
is replaced by a completely new DOM element. This event then also causes any row-level state affected by the cell-level state to become in sync again.
I don't know how I would have solved this is the content of one cell (like a validity icon) depended on the content of another (like a text field). There is no way that I can see to keep row-level live state without re-rendering the whole table each time. In my case fortunately, the cell-level state tracking while editing and table-level state update when finished editing the cell were all I needed.
Hopefully this helps someone else.
I had the same issue using
useQuery
with my table. The problem was that I was setting data to a default[]
when pulling fromuseQuery
:const {data = []} = useQuery( ... )
The fix was to do this logic inside the play where I was memoizing my data:
const {data: theData} = useQuery( ... ) const data = React.useMemo(() => { return Array.isArray(theData) ? theData : []; }, [theFiles]); const columns = React.useMemo<ColumnDef<File>[]>( () => tableColumns.map(column => COLUMNS[column]), [tableColumns] );
Wrapping useMemo() to those props does help. Thank you for your inspiration. @megancooper
For my case, I have a schema applied to each cell with an input. If a validation fails for an input then the cell will rerender with an error message. This rerender will 'blur' my input there.
My guess is that, by wrapping useMemo() to those props,it will only rerender part of the component inside the cell instead of the whole cell.
add these on your useReactTable
autoResetPageIndex: false, autoResetExpanded: false,
add these on your useReactTable
autoResetPageIndex: false, autoResetExpanded: false,
Those options should have nothing to do with infinite re-renders. Failing to give columns and data a stable reference will though.
add these on your useReactTable
autoResetPageIndex: false, autoResetExpanded: false,
it work for me
In my case the problem was caused not by
data
but bycolumns
. TheDataTable
hadinput
s in its cells which had a draft state that I was tracking at the level of the component that was rendering theDataTable
. On each change of theinput
the draft state would update and that would update thecolumns
(such that the new cells would contain newinput
s with the correct draft value. This works fine in a straight HTML table assuming your keys are set up properly, but inDataTable
thecolumns
seem to need to be referentially equal or the whole table re-renders completely (not just reconciles the VDOM changes to the DOM) and theinput
that is being edited loses focus (because it is replaced by a newinput
with new text).I solved this by moving the draft state into its own component used at the cell level. The component is returned by
cell
incolumns
and takes the table row's model but encapsulates the draft state completely within itself. When the editing is done (oninput
blur
or when the Enter key is pressed) I go and update the backing data source which invalidatesdata
and re-renders the whole table clearing theinput
s draft state because again at that point theinput
is replaced by a completely new DOM element. This event then also causes any row-level state affected by the cell-level state to become in sync again.I don't know how I would have solved this is the content of one cell (like a validity icon) depended on the content of another (like a text field). There is no way that I can see to keep row-level live state without re-rendering the whole table each time. In my case fortunately, the cell-level state tracking while editing and table-level state update when finished editing the cell were all I needed.
Hopefully this helps someone else.
this is very similar to what I'm facing right now. My table cells have input cells, datePickers e.t.c that when updated, call an onChange function that updates the database and invalidates a query key, which changes the state and I assume causes the whole table to rerender
Could you share some more specific info on this? maybe some code perhaps
Could you share some more specific info on this? maybe some code perhaps
+1
I'm not sure if it's the same issue, but I solved the excessive re-rendering issue that occurred when using @tanstack/react-table by using @tanstack/react-virtual