recyclerlistview icon indicating copy to clipboard operation
recyclerlistview copied to clipboard

Can stableId be used with lists where new data is appended (at the top) ?

Open Codelica opened this issue 4 years ago • 1 comments

I have a data array that renders fine with RLV using a simple data provider (without stableId) like:

new DataProvider(
  (r1, r2) => r1._id !== r2._id
).cloneWithRows(alertArray);

However my "alertArray" changes over time. Basically new items come in and the last 100 items are kept. As new data comes in (over a WebSocket) it's added to the top of the array (index 0, via unshift()) and if the length is over 100 it's clipped to 100.

So when a new item comes in, the list is re-rendered properly. However it seems to trigger a complete re-render including several list items (that haven't changed other than being lower in the list) and the view is reset to the top of the list -- instead of just rendering the new element being added ? (like it does in flatlist for me)

Looking further it seemed like the stableId feature might help with this. (am I wrong?). All of my list items have a unique id, so I added it like:

new DataProvider(
  (r1, r2) => r1._id !== r2._id,
  index => alertArray[index]._id
).cloneWithRows(alertArray);

However using stableId, when a new item is unshifted to the top of the array it gets rendered along with a bunch of warnings and an error regarding "Possible stableId collision" and rendering of a duplicate key (which is not possible with my data). It also leaves a "missing" item in the list relative to where I'm scrolled when the addition comes in. (presumably the "duplicate" which is not possible with my data).

Trying to look further I see an old discussion on #201. Which has left me even more confused. Especially the comment by @naqvitalha which seems to describe exactly what I'm seeing as being normal and nothing to worry about.

So, I've figured out the issue. Consider the following example:

Stable Ids (Scenario 1):
1, 2, 3, 4, 5, 6, 7, 8, 9

Visible Indices: 1 to 7

Post Data Change (1 item got inserted):
0, 1, 2, 3, 4, 5, 6, 7, 8, 9

Visible Indices: 0 to 6

For perf reasons we don't scan full data on every change. Here RLV thinks that index 7 is being discarded so, it utilises it to render the new id 0. So, the insertion will not require any mounts. However, now when you scroll down there is no item in recycle pool and 7 isn't available for rendering. RLV thinks of it as an anomaly and creates a new recycling key using an internal logic. Now this is perfectly fine and, certainly, is an optimal way to deal with the change. The warning is incorrect though. There is nothing to worry about. It can be removed I think. You can also enable optimizeForInsertDeleteAnimations={true} to prevent RLV from being too aggressive about reuse.

So I'm definitely confused about stableId and what the best route is to minimize renders on a data set that changes over time with additions placed at index 0.

Any suggestions or comments would be welcome!

Codelica avatar Sep 04 '21 00:09 Codelica

Well to start I took the sample Snack for RLV that displays a list of dogs (https://snack.expo.io/@naqvitalha/rlv-demo) and extended it to add a single new tog to the top of the list every 5 seconds on this updated Snack: https://snack.expo.dev/@jpooton/rlv-demo

From the console.log on the snack I can see it re-rendering 5 previous dogs in addition to the one new dog added each interval. So it seems the that is the normal behavior, at least without using stableId.

I'd love to hear if there is a way to add items to the top of the data array without a re-render of the previous items in the window, as that seems to be the one strength I had with FlatList.

Edit: Actually it looks like the render function is called for the previous images, but they aren't actually re-rendered because of shouldComponentUpdate. So it looks like I'll have to dig into why my hook based use isn't behaving the same. Just talking to myself :)

Final Edit: Well... after further experiments, it seems like this must be inherit in the design, as I am seeing the additional renders whenever items are added above or within the visible window -- both using class based RLV or hook based RLV. It seems as though the layout is preserved and all the items are shifted within it (causing re-renders) vs the new element being added to the layout. At least thats my suspicion at this point. I'm on a deadline and since our app has all items appended at the top. Since FlatList doesn't have this behavior I'll have to stick with that. But I would love to know if I'm doing something wrong or if something like the stableId option would be the answer for this. I tried using it but ran into the collisions like others have, which aren't possible with my ids. It looks like there is a PR pending, so maybe down the line.

Codelica avatar Sep 04 '21 15:09 Codelica