Description:
After updating react-table from 7.0.0-rc.16 to 7.0.0, I am getting an error when I try to expand a row Error: Maximum update depth exceeded.
Codesandbox example:
https://codesandbox.io/s/lucid-bartik-u56pc
Steps to reproduce the behavior
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.
Expected behavior
react-table from the left-side panel to 7.0.0-rc.16Screenshots

I can confirm that this does appear to be a problem isolated to react-table. For some reason, it seems like quite a few library authors put out breaking changes in the last few days. Updating to all of the newest versions doesn't cause a problem, except for react-table.
7.0.0 also broken for me, same error.
I got the same error with 7.0.0
Yep, it appears the 7.0.0 update broke for me too
This was happening to me as well during other state changes (misc react hook calls) ..
Downgrading to 7.0.0-rc.16 resolved the issue for me
@Nizar-Rahme In your CodeSandbox example, you need to memoize expanderCol in Table.js. I did that and I don't see the error any more: https://codesandbox.io/s/keen-sun-jgwrd.
I had this issue, when I didn't memoize both columns and data. Memoizing both fixed if for me.
I had the same error but was not using useExpanded. If I went back to 7.0.0-rc.16 it was fine, broken in 7.0.0.
I then saw @timothymathison's comment above, and saw that the columns weren't memoized. This fixed it for me.
Yes, for me use a useMemo on columns fix the problem !
Fixed for me also memoizing columns data.
I had the same error but was not using useExpanded. If I went back to 7.0.0-rc.16 it was fine, broken in 7.0.0. I then saw @timothymathison's comment above, and saw that the columns weren't memoized. This fixed it for me.
Same. Wasn't using useExpanded but it's still broke.
Thank you everyone for the potential fix, but this still seems like a problem with the library. I don't remember anywhere in the documentation that it says you must memoize columns. Even if it did, why should this be required. Personally I'm going to stay on 7.0.0-rc.16 until this is fixed properly.
I agree that this is still a bug with react-table. 👍
I mainly provided the memoization suggestion as a fix which acts as temporary workaround (although I think its a good practice in general).
Thank you everyone for the potential fix, but this still seems like a problem with the library. I don't remember anywhere in the documentation that it says you must memoize columns. Even if it did, why should this be required. Personally I'm going to stay on
7.0.0-rc.16until this is fixed properly.
It is actually in the documentation (https://github.com/tannerlinsley/react-table/blob/master/docs/api/useTable.md#table-options):
columns: Array<Column>
- Required
- Must be memoized
That being said, I agree that it is a usability issue but it is broader than just columns - there are many arguments that get passed into useTable that need to be memoized for everything to work properly or be performant. This is also an issue for many functional React components that use hooks; and as much as I like functional components and React hooks, it gets pretty annoying to have to wrap everything in useMemo or useCallback.
Without memoizing columns and other data structures, the table lifecycle would require a lot of manual intervention to update. You would end up with 10x more issues that are instead centered around the table not updating when you changed something and expected it to.
there are many arguments that get passed into useTable that need to be memoized for everything to work properly or be performant.
I don't know the underlying architecture of this library, but shouldn't not memoizing something simply just result in worse performance, rather than a stack-overflow-type error. That's what it seemed to do in 7.0.0.rc-16. I don't know of any other library that requires memoization or else risk a crash.
Quick answer:
I can say with 100% certainty that if you are not memoizing your columns or data, you are not using React Table correctly. Everything in the docs and every example instructs you how to do this (as far as I'm aware). This is required so that React Table can knowingly optimize as much as computationally expensive work as possible for you while rendering the least amount possible.
Long answer:
I agree that a library should handle non-memoized values as best as possible, and React Table does that where possible. You'll notice that for many values, you don't have to memoize them even though the docs say too, and you'll simply be opting out of a ton of performance optimizations. Your app may even run smoothly still in some cases. However, even in your own code, there are situations where you must memoize certain values or functions to avoid stack overflow. For example, it's easy to see that if you don't memoize incrementCount below, or set up some kind of condition to call it, you will stack overflow.
const [count, setCount] = React.useState(0)
const incrementCount = () => setCount(old => old + 1)
React.useEffect(() => {
incrementCount()
}, [incrementCount])
This is in your own code, too, so even without React Table in the equation, you are still susceptible to this issue if you do not program for closures, rerenders, and memoization. When you develop a library like React Table that must be as performant as possible, you must rely on the built in performance mechanisms of the platform and their rules (React's built-in useMemo and useCallback hooks in this case).
Unfortunately, there is no way to know for certain from within React Table if a value or callback is truly memoized (wow that would be great), and until that happens, it will be a possibility for people to not understand the documentation and/or API and not provide stable values/functions to React Table. At the end of the day, I can't only do so much to ensure that users aren't recreating values and functions on every render.
On the bright side, if you truly are sending in stable values to React Table and getting infinite rerenders or stack overflow, then you have absolutely found a bug and I will happily fix it! 😄
With that said, if you are sending in memoized values that you know are only changing when they should and still experiencing this bug, feel free to open a new issue with a codesandbox example demonstrating the issue.
Thanks for the answers, and yes, memoizing solved my issue.
yeah, properly memoizing solved this for me too.
I had some... non obvious code that was breaking the memoization
const dependency = cleanArray(...)
const columns = React.useMemo(() => {}, [dependency])
where my cleanArray function was returning a new instance of the array every render.
Silly me 🤦♂️
React documentation here clearly says:
You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.
So I would consider this behaviour a bug in a library.
@Azatik1000 Requiring memoized values in a library is very normal. The React documentation is still 100% correct, you cannot rely on a value always being 100% memoized across renders, and that's fine. React Table can handle the situation that it gets skipped or forgotten. What React Table does require is that your data and column references are stable and only change when necessary. It is just convenience that useMemo allows you to ensure this in the easiest way. You could use state, other memoization libraries, or even a constant outside of the render cycle to define your data and/or columns, but that probably won't always scale to your use cases, especially if your table configuration changes over time.
I had the issue after adding useMemo and adding empty array or dependecy array fixed issue
Most helpful comment
Quick answer:
I can say with 100% certainty that if you are not memoizing your columns or data, you are not using React Table correctly. Everything in the docs and every example instructs you how to do this (as far as I'm aware). This is required so that React Table can knowingly optimize as much as computationally expensive work as possible for you while rendering the least amount possible.
Long answer:
I agree that a library should handle non-memoized values as best as possible, and React Table does that where possible. You'll notice that for many values, you don't have to memoize them even though the docs say too, and you'll simply be opting out of a ton of performance optimizations. Your app may even run smoothly still in some cases. However, even in your own code, there are situations where you must memoize certain values or functions to avoid stack overflow. For example, it's easy to see that if you don't memoize
incrementCountbelow, or set up some kind of condition to call it, you will stack overflow.This is in your own code, too, so even without React Table in the equation, you are still susceptible to this issue if you do not program for closures, rerenders, and memoization. When you develop a library like React Table that must be as performant as possible, you must rely on the built in performance mechanisms of the platform and their rules (React's built-in useMemo and useCallback hooks in this case).
Unfortunately, there is no way to know for certain from within React Table if a value or callback is truly memoized (wow that would be great), and until that happens, it will be a possibility for people to not understand the documentation and/or API and not provide stable values/functions to React Table. At the end of the day, I can't only do so much to ensure that users aren't recreating values and functions on every render.
On the bright side, if you truly are sending in stable values to React Table and getting infinite rerenders or stack overflow, then you have absolutely found a bug and I will happily fix it! 😄
With that said, if you are sending in memoized values that you know are only changing when they should and still experiencing this bug, feel free to open a new issue with a codesandbox example demonstrating the issue.