-
Notifications
You must be signed in to change notification settings - Fork 429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Warn: Possible stableId collision #201
Comments
Returning the same stableId for two different items? Any repro steps or expo snack? |
@naqvitalha In that and the fact that I get a list of messages from the server and there are no duplicate IDs, there is a suspicion that the place of the key he sometimes puts not stableid but the element type... |
Can you share you data provider and row renderer if possible? Also make sure stableId is a string |
I also sometimes get this even if i do not add |
hey! i think i have 'something' about this issue. context: i'm on react-native using RLV latest beta. the issue arises when we're updating the dataset, adding new elements to the 'head', i mean: ['id_10', 'id_9', 'id_8', 'id_7', ...] => ['id_11', 'id_10', 'id_9', 'id_8', 'id_7', ...]. i think the problem is within
when you assign
maybe i'm wrong -so forgive me in that case!-, but:
hope this helps! |
hey @naqvitalha , i've solved this :) if you have time you can check this out: if you're opened to PR, it'll be awesome if i can contribute. also FYI, if you check my fork branch you'll see i've added support for custom row / cell component holder since i'm working with the amazing react-native-reanimated library and -as it happens w/ other animation frameworks- you need to use a custom please, let me know your thoughts. and enjoy your day! |
Sadly, but the feeling that the project was abandoned ((( |
Animations do not work correctly, because of replacing the appearance of the block occurs not from the bottom up, and first from the top Inthey and then immediately from the bottom up, because the new element uses the newly vacant cell |
honestly i don't think it's abandoned, actually you can see recent activity in at least another branch. that being said, the core feature works really well, and i understand that was the main priority. animations and performance fine tuning is happening (and it's still in beta). about animation, i'm not understanding you, sorry. i'm developing a im interface and, using reanimated as i mentioned, and after understanding the logic behind dataset updates i'm getting very good results :) |
bad news... the fix was not a fix... as soon as you scroll up, then down, then change the dataset the warn is still there... and i'm not able to find time to understand what is happening. one thing is clear btw: the problem arises whenever you change the dataset 'moving' items, like shifting an array. with the simple chat example: whenever you receive new messages. it works ok if you add past messages. @naqvitalha could you please try to explain some core concepts (like the render stack, etc.) in order to let me take a deeper look to this? |
One thing I can think of is that you might be mutating the same array that you passed to DataProvider. Ideally according to immutability principle you shouldn't. Try cloning instead and see. If it doesn't help try creating a repro on expo and I will look into it ASAP. |
And the project is very much active. We just released two huge projects on top of this. Beta is much more active :) |
actually i'm using immer for immutability, passing the new state array to cloneWithRows, so i think that's not the problem. i'll tell you more: i've tried to assign a new getStableId to the new DataProvided returned by the mentioned method and i've could check that it's trying to get stable ids from indexes that didn't exist in the previous state. eg first state is an empty dataset, then 10 items, you can see how it's trying to get stable ids from empty dataset! |
anyway, i'll try to make an expo hello world trying to reproduce the problem |
morning! expo is taking ages to publish, so as it's just 3 files i've made a gist: i'm using the sample component from RLV guide, with a few changes:
in order to reproduce the problem:
BUT, eg
there's more random situation where it seems to works, but then you scroll or something and there it is again. what do you thing @naqvitalha ? thanks! |
@naqvitalha I'm also having this same warning. The weird thing is in my case only happens with the first rows and doesn't happen all the time. What I did notice is that when I first load the list, the first key on the renderStack is at dataIndex: 0, after a few scrolls loading more data, that same key ends up in the dataIndex: 35 and then throws the warning saying that key already exists on the renderStack |
yes @tafelito, it seems the same bug. i also traced the render stack and noticed the same dataIndex stuff. |
I'm investigating. Let me get back. |
nice @naqvitalha! |
So, I've figured out the issue. Consider the following example:
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. |
@naqvitalha When to wait for the new version of Beta? |
Closing as this has been fixed in the latest release. |
Hey guys, I have same issue. Installed latest Beta version and also tested on stable. |
I'm having this issue, only when I specify the const dataProvider = useMemo(
() =>
new DataProvider(
(item1: Hit, item2: Hit) => item1.objectID !== item2.objectID,
(index) => hits?.[index]?.objectID // this causes the error
).cloneWithRows(hits ?? []),
[hits]
) I can confirm with certainty that my list does not have any duplicates, nor am I mutating it at any time. This is my full code: // ...imports
export default function FastArtistHitsCards(props: Props) {
const { hits, fetchMore } = props
const { width, height, onLayout } = useLayout()
let itemsPerRow = 2
if (width >= defaultBreakpoints[1]) {
itemsPerRow = 3
}
const itemWidth = width / itemsPerRow
const itemHeight = itemWidth * 1.5
const dataProvider = useMemo(
() =>
new DataProvider(
(item1: Hit, item2: Hit) =>
// define that two items are not the same
item1.objectID !== item2.objectID,
// get the stable id for a given item
(index) => hits?.[index]?.objectID
).cloneWithRows(hits ?? []),
[hits]
)
const layoutProvider = useMemo(() => {
return new LayoutProvider(
(index) => {
return 'card' // every item is the same
},
(_, dimensions) => {
dimensions.width = itemWidth
dimensions.height = itemHeight
}
)
}, [itemHeight, itemWidth])
// we need to re-render if the width changes
const extendedState = useMemo(() => ({ width }), [width])
const renderList = () => {
if (!width || !hits?.length) return <LoadingScreen />
return (
<RecyclerListView
dataProvider={dataProvider}
layoutProvider={layoutProvider}
extendedState={extendedState}
rowRenderer={(_, artist, index) => {
return (
<ArtistCardFixedSize
artist={artist}
height={itemHeight}
width={itemWidth}
/>
)
}}
style={{
height,
width,
}}
scrollViewProps={{
showsVerticalScrollIndicator: false,
keyboardDismissMode: 'on-drag',
keyboardShouldPersistTaps: 'handled',
}}
onEndReached={fetchMore}
/>
)
}
return (
<View sx={{ flex: 1 }} onLayout={onLayout}>
{renderList()}
</View>
)
} I'm a bit confused as to why this is happening. I am certain that the ID I'm returning is unique in the list. I really need to use this field, because it seems as though the recycling is causing my images to flicker. When I first scroll to an item that is far down, it shows up as the wrong image's index, and then it changes to the correct one. I assume this is because of the unique keys being wrong. |
I think I identified the issue: const dataProvider = useMemo(
() =>
new DataProvider(
(item1: Hit, item2: Hit) => item1.objectID !== item2.objectID,
(index) => hits?.[index]?.objectID // this causes the error
).cloneWithRows(hits ?? []),
[hits]
) Even when I infinite scroll and get new hits, it is still referencing the original hits object. I even tried to move this into a ref, so that it always accesses the latest const hitsRef = useRef(hits)
useEffect(() => {
hitsRef.current = hits
})
const dataProvider = useMemo(() => {
console.log({ hits }) // this logs correctly!
return new DataProvider(
(item1: Hit, item2: Hit) => item1.objectID !== item2.objectID,
(index) => {
// these only log correctly on the first render.
console.log({ hits, hitsRef })
hitsRef.current?.[index]?.objectID // changed here
}
).cloneWithRows(hits ?? [])
}, [hits]) What's weird is, in the stable ID function, items that do indeed exist in the |
@nandorojo did you end up finding a solution to this? I'm running into the same problem.
@arunreddy10 perhaps you can reopen the issue? I'm on version |
No, I ended up not using recycler list. I tried for a while but could never get it to work. Found the API pretty confusing too. Wish I could help. |
@nandorojo that's a pity, thank you for replying though! Unfortunately the way to go seems to not use stable ids at all. |
@lucasbento I was just able to work around this using a ref. I'm using a class component manually assigning like this:
I'm not certain why @nandorojo's solution didn't work, but I suspect
|
I did not got how first solution is working. Can you give a full sample code ? |
I did this here: https://github.com/daniel-centore/WikiSpiv-App/blob/680e5808b67788b6e2c8b28b1460c8e4f7d3cfe8/components/FlatListQuickScroll/FlatListQuickScroll.tsx |
@daniel-centore Thanks |
@daniel-centore @davx1992 Warning: Encountered two children with the same key, Can you please tell any solution ? |
@siddharth-kt I am not a recyclerlistview dev nor am I an expert in it, just someone who has used it before. You are welcome to look at the code I linked to above for a working example. If you need debug support you should probably include a minimum example demonstrating the issue. I cannot promise I'll have time to look at it, but it would be useful for others too. |
Folks, I'm looking into this. Will update the issue soon. |
Thanks @daniel-centore Hi @naqvitalha thanks for looking into this,
kindly see this issue. Example code : <RecyclerListView |
Hello folks, do me a favour and try version 3.1.0-alpha.7. The issue should be fixed and warning should only show up if there are legitimate duplicates. Watch out for the following:
I'd also appreciate if someone can try this with reanimated layout animations. Thanks! |
Hi @naqvitalha, But i am still facing below mentioned issue 🤔 (when deleting cells) Warning: Encountered two children with the same key, Any solution for this ? |
Can you try recreating a repro in expo and share? The error that you mentioned is not raised by recyclerlistview so it could be an issue with one of your components passing duplicate keys to react. |
Thanks @naqvitalha, I think I got the point. When ever i delete or add a element/cell in RecyclerListView dataProvider then new cell uses state values (example, To solve this issue I modified dataProvider like : Now i changed my every cell to re-render with new data.id updates, example. Am i doing it the correct way or is there any more optimized way of doing this ? Thanks |
Having the same issue. |
Have the same problem with 3.3.0-beta. On remove prepareForLayoutAnimationRender method was called, but still have no animation on IOS. |
@AlexSirenko Can you provide an expo sample? I can take a look. |
Edit: Here it is: 🖕 I wish there was a middle finger emoji for how much time I've wasted on this non-functioning library. |
For anyone in search of alternatives, FlashList (from Shopify) has been working well for me. |
Came here looking for the same warning happening with FlashList. 😆 |
@TheJanitor337 same, did you ever find a solution? |
@TheJanitor337 did you ever managed to get around this? thanks |
@angelxmoreno @efstathiosntonas Sorry for the delay! I didn't find a solution for this, but didn't debug very much as it was just a coding challenge project. I'll try to look at it this weekend to see if I can reproduce/fix what was happening. If I can, I'll share here. This FlashList GitHub issue might be of help: Shopify/flash-list#730 |
@TheJanitor337 What I did and I've never encountered the stableId collision is to add the index to the const keyExtractor = useCallback((item: any, i: number) => `${i}-${item.id}`, []); I was constantly having issues with it since my list is 100% real-time but now the issues are gone. Can't explain why adding the index fixes it, it seems it works automagically now 🤷 |
This works by essentially making the key different every time the list changes so recycling is effectively disabled. |
**VirtualRenderer.prototype.syncAndGetKey **
In line
if (stackItem && stackItem.dataIndex !== index) {...}
Sometimes the key is equal to type ( 0 for me ), How do I fix this warning?
The text was updated successfully, but these errors were encountered: