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 inputs 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 inputs 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 inputs 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
useQuerywith 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
databut bycolumns. TheDataTablehadinputs 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 theinputthe draft state would update and that would update thecolumns(such that the new cells would contain newinputs with the correct draft value. This works fine in a straight HTML table assuming your keys are set up properly, but inDataTablethecolumnsseem to need to be referentially equal or the whole table re-renders completely (not just reconciles the VDOM changes to the DOM) and theinputthat is being edited loses focus (because it is replaced by a newinputwith new text).I solved this by moving the draft state into its own component used at the cell level. The component is returned by
cellincolumnsand takes the table row's model but encapsulates the draft state completely within itself. When the editing is done (oninputbluror when the Enter key is pressed) I go and update the backing data source which invalidatesdataand re-renders the whole table clearing theinputs draft state because again at that point theinputis 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