You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
React v3.13.13 causes infinite re-renders after setting getItemKey() #1092
As title. Only happens after going from v3.13.12 to v3.13.13.
Your minimal, reproducible example
See below.
Steps to reproduce
Run the following code, which is grabbed from the example with just an getItemKey() addition
import { useVirtualizer } from '@tanstack/react-virtual';
function App() {
// The scrollable element for your list
const parentRef = React.useRef(null)
// The virtualizer
const rowVirtualizer = useVirtualizer({
count: 10000,
getScrollElement: () => parentRef.current,
estimateSize: () => 35,
+ getItemKey: (index) => index,
})
return (
<>
{/* The scrollable element for your list */}
<div
ref={parentRef}
style={{
height: `400px`,
overflow: 'auto', // Make it scroll!
}}
>
{/* The large inner element to hold all of the items */}
<div
style={{
height: `${rowVirtualizer.getTotalSize()}px`,
width: '100%',
position: 'relative',
}}
>
{/* Only the visible items in the virtualizer, manually positioned to be in view */}
{rowVirtualizer.getVirtualItems().map((virtualItem) => (
<div
key={virtualItem.key}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualItem.size}px`,
transform: `translateY(${virtualItem.start}px)`,
}}
>
Row {virtualItem.index}
</div>
))}
</div>
</div>
</>
)
}
Expected behavior
No error.
How often does this bug happen?
Every time
Screenshots or Videos
Platform
Windows 11 Edge (But I guess all browsers have this issue)
tanstack-virtual version
v3.13.13
TypeScript version
No response
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.
it was mentioned that getItemKey must be memoised, but what about other two? Is it possible to know which must be memoised from documentation?
I came to this report because I also have the same problem, but I do not use getItemKey and desperately try to find which of the options to blame 🤷
This needs someone authentic enough to confirm about how it should work, but what I think should work is getItemKey() shouldn't also be memorized as well. It's more like a bug introduced in this version. For the other 2, the tutorial and my usages have them un-memozied and so far so good
The documentation does mention that getItemKey needs to be memoized, but I had missed it and it wasn't causing trouble before.
I'm fine if the solution is to memoize it, but in addition to the documentation mentioning it, I think there should be examples of it in use. I also wish it was more clear what needs to be memoized, because I pass a lot of unmemoized functions to createTable and they don't trigger rerenders..
The infinite re-rendering issue was introduced in #1085
@piecyk updated the docs to include a note about memoizing getItemKey in #1103. I'd consider this a breaking change, since this memoizing requirement was not necessary before 🤷♂️
@lott-ai Yeah, my bad, I agree this should be released as a major version. Even though the fix itself is reasonable, requiring getItemKey to be memoized wasn’t necessary before, and existing users can now run into infinite re-renders without any obvious indication of why.
Personally, I’ve always memoized getItemKey since it’s part of the caching mechanism. There was already a real performance issue there, which is probably why I didn’t notice this earlier. Still, changing this behavior without a major release was a bad call, and it should be clearly communicated.
May I know why memoizing getItemKey() is strictly needed?
And if it's strictly necessary, can we make it memoized in the implementation directly, saving us from not knowing that we need to memoize? Because apparently there are people not knowing this nuances, including myself. It was also unexpectedly working before.
May I know why memoizing getItemKey() is strictly needed?
@RaenonX the virtualizer automatically invalidates its measurement cache when measurement-affecting options change (for example, getItemKey). So if it isn’t memoized, every re-render recreates the list where the elements are placed.
Overall the fix was unnecessary, getMeasurements() already invalidates on count changes via getMeasurementOptions() #1112
May I know why memoizing getItemKey() is strictly needed?
@RaenonX the virtualizer automatically invalidates its measurement cache when measurement-affecting options change (for example, getItemKey). So if it isn’t memoized, every re-render recreates the list where the elements are placed.
Overall the fix was unnecessary, getMeasurements() already invalidates on count changes via getMeasurementOptions() #1112
I see, thanks for the clarification. Can I assume that after the next release (the PR you just created), memoization from the user is not needed anymore?
I see, thanks for the clarification. Can I assume that after the next release (the PR you just created), memoization from the user is not needed anymore?
Yep, it won’t be needed anymore, but it’s still highly recommended. Let’s save some RAM 🙂
Overall, sorry everyone for the hassle.
Reacted by RaenonX, Ivan Kurnosov and Reece Carolan
Activity
j-marty commentedon Dec 12, 2025
Move inline function for getItemKey to a callback function as a workaround. Like this:
const getItemKey = useCallback((idx: number) => {...}, []);J4v4Scr1pt commentedon Dec 12, 2025
Same here, above solution worked for me as well. 👍
israel-lpz commentedon Dec 13, 2025
Same here, above solution worked for me
marcelpanse commentedon Dec 14, 2025
Came here for the same bug. Putting it in a useCallback worked.
Edit: we still had some problems in production with some users so we had to revert back to 3.13.12. This change breaks backwards compatibility.
channyeintun commentedon Dec 16, 2025
Even with React Compiler, I still have to wrap it up inside useCallback?
RaenonX commentedon Dec 16, 2025
Using default strategy (syntax inferred or something), if you are not wrapping the function in a
use*hook then yeszerkms commentedon Dec 19, 2025
Now with this release it's now unclear what functions should be memoised, and which not.
Eg, in the first post example:
it was mentioned that
getItemKeymust be memoised, but what about other two? Is it possible to know which must be memoised from documentation?I came to this report because I also have the same problem, but I do not use
getItemKeyand desperately try to find which of the options to blame 🤷RaenonX commentedon Dec 19, 2025
This needs someone authentic enough to confirm about how it should work, but what I think should work is
getItemKey()shouldn't also be memorized as well. It's more like a bug introduced in this version. For the other 2, the tutorial and my usages have them un-memozied and so far so goodzerkms commentedon Dec 19, 2025
Ok, I found what change happened to me: now (in 3.13.13) the
onChangehandler IS triggered when number of rows changes (no actual scrolling).And in 3.13.12 it DID NOT trigger 🤷
mallio commentedon Dec 22, 2025
The documentation does mention that getItemKey needs to be memoized, but I had missed it and it wasn't causing trouble before.
I'm fine if the solution is to memoize it, but in addition to the documentation mentioning it, I think there should be examples of it in use. I also wish it was more clear what needs to be memoized, because I pass a lot of unmemoized functions to createTable and they don't trigger rerenders..
channyeintun commentedon Dec 23, 2025
It was working fine without using useCallback in the previous version. I just installed the latest version and got this issue. It is BREAKING CHANGES?
zerkms commentedon Dec 23, 2025
@channyeintun given it's been documented - it's not a BC but a bug fix.
lott-ai commentedon Jan 7, 2026
The infinite re-rendering issue was introduced in #1085
@piecyk updated the docs to include a note about memoizing
getItemKeyin #1103. I'd consider this a breaking change, since this memoizing requirement was not necessary before 🤷♂️piecyk commentedon Jan 7, 2026
@lott-ai Yeah, my bad, I agree this should be released as a major version. Even though the fix itself is reasonable, requiring getItemKey to be memoized wasn’t necessary before, and existing users can now run into infinite re-renders without any obvious indication of why.
Personally, I’ve always memoized getItemKey since it’s part of the caching mechanism. There was already a real performance issue there, which is probably why I didn’t notice this earlier. Still, changing this behavior without a major release was a bad call, and it should be clearly communicated.
zerkms commentedon Jan 7, 2026
@lott-ai it was documented even before #1103: piecyk@106a0be
RaenonX commentedon Jan 7, 2026
May I know why memoizing
getItemKey()is strictly needed?And if it's strictly necessary, can we make it memoized in the implementation directly, saving us from not knowing that we need to memoize? Because apparently there are people not knowing this nuances, including myself. It was also unexpectedly working before.
piecyk commentedon Jan 7, 2026
@RaenonX the virtualizer automatically invalidates its measurement cache when measurement-affecting options change (for example, getItemKey). So if it isn’t memoized, every re-render recreates the list where the elements are placed.
Overall the fix was unnecessary, getMeasurements() already invalidates on count changes via getMeasurementOptions() #1112
RaenonX commentedon Jan 7, 2026
I see, thanks for the clarification. Can I assume that after the next release (the PR you just created), memoization from the user is not needed anymore?
piecyk commentedon Jan 7, 2026
Yep, it won’t be needed anymore, but it’s still highly recommended. Let’s save some RAM 🙂
Overall, sorry everyone for the hassle.
RaenonX commentedon Jan 7, 2026
No problem, and thanks for handling this! <3