react-spectrum icon indicating copy to clipboard operation
react-spectrum copied to clipboard

Table: Duplicate item id causes infinite collection and out of memory crash

Open nathanforce opened this issue 9 months ago โ€ข 7 comments

Provide a general summary of the issue here

After a recent react-aria upgrade we started seeing our Node servers tip over due to running out of memory. We managed to track the cause to be how we were constructing rows in our RAC based Table component.

Specifically, two or more rows sharing a single id when set via props on the Row element is where we saw the problem.

๐Ÿค” Expected Behavior?

Maybe we should warn in this case? Or ignore subsequent items with conflicting keys?

๐Ÿ˜ฏ Current Behavior

Page/app crashes once the array grows too large.

๐Ÿ’ Possible Solution

No response

๐Ÿ”ฆ Context

No response

๐Ÿ–ฅ๏ธ Steps to Reproduce

https://codesandbox.io/p/sandbox/winter-moon-szxxyj

Version

[email protected]

What browsers are you seeing the problem on?

Chrome

If other, please specify.

Also in Node during SSR

What operating system are you using?

macos

๐Ÿงข Your Company/Team

No response

๐Ÿ•ท Tracking Issue

No response

nathanforce avatar Apr 04 '25 17:04 nathanforce

We do specifically say https://react-spectrum.adobe.com/react-aria/collections.html#unique-ids

I think we've discussed throwing an error if we see this, or a warning, like React does for duplicate keys.

We definitely don't want to ignore them, as that would be akin to failing silently and then users may not realize that they are missing data.

snowystinger avatar Apr 07 '25 01:04 snowystinger

Given that the result here is a total crash of the browser tab (or the server in SSR), I don't think a warning is sufficient unless you somehow break the loop.

Agree that ignoring items is suboptimal and unexpected, but so is the current behavior.

nathanforce avatar Apr 07 '25 13:04 nathanforce

This affects not just Tables, but collections in general. The most insidious part of this bug is that it does not point to the part of your code responsible for the error, it just points here once it exceeds max array size.

If possible, I'd rather see RAC crash sooner, because the slowdown leading up to the crash, as memory is depleted, makes the whole experience feel even worse.

staticshock avatar Jun 09 '25 19:06 staticshock

I'm struggling to add an equivalent [generic] check in userland for this basic example:

<MyCollection items={items}>
  {(item) => <MyCollectionItem id={item.someId}>{item.value}</MyCollectionItem>}
</MyCollection>

Is there some way to generically catch duplicate ids inside MyCollection or MyCollectionItem? From the point of view of a single item I don't have any context on what other items got rendered, and from the point of view of the collection I don't know how the ids are going to be derived.

staticshock avatar Jun 09 '25 20:06 staticshock

can you explain more about this comment?

from the point of view of the collection I don't know how the ids are going to be derived

I would expect that you can do something like (psuedocode)

useMemo(() => {
  let keys = new Set();
  for (let item of items) {
    if (keys.has(item.id /* or whatever your unique identifier is */) {
      throw, or return false or whatever you want to do
    }
    keys.add(item.id);
  }
}, [items])

If someone would like to contribute a check for this in our code, I think it shouldn't be too hard, just check if the key already exists in the keyMap and throw if it does https://github.com/adobe/react-spectrum/blob/d312e0a200f54ba6a9dcb7588d55731c191e5d4f/packages/%40react-aria/collections/src/BaseCollection.ts#L195

snowystinger avatar Jun 09 '25 21:06 snowystinger

What I mean is that inside <MyCollection> I don't know what the id field name is going to be. In my example above, the key was item.someId, not item.id. Basically, it gets kind of ugly if the field name of the id field is not hardcoded to item.id. Otherwise I'd agree with you.

staticshock avatar Jun 10 '25 03:06 staticshock

I tried your suggestion of throwing an exception in BaseCollection, but it breaks hundreds of tests. Hopefully all of those failures can be fixed by updating fixtures, but I'm a bit out of my depth on making sure none of the failures are legitimate.

staticshock avatar Jun 10 '25 04:06 staticshock