flash-list: [Android] FlashList is caching the wrong state for its items

Current behavior

FlashList is caching the wrong state for its items. This issue occurs with lists containing around 40 items or more. In this example, the checkbox should default to ‘off’ based on the boolean state. However, for some reason, the state change every time the list updates. This issue specifically affects the item at the end of the list. This is not the first time I’ve encountered this problem; it happened before when I was using a state URL for the image component.

I tested it with FlatList, and it doesn’t have this issue.

This issue is occurring in both debug and production builds.

https://github.com/Shopify/flash-list/assets/58077449/a9f8bfd5-b4c3-4ca8-a4e7-e39abd26870f

Platform:

  • Android paper

Environment

@shopify/flash-list that v1.5.0

### Tasks

About this issue

  • Original URL
  • State: closed
  • Created 10 months ago
  • Reactions: 1
  • Comments: 17

Most upvoted comments

Adding “key={someId}” prop to the rendered item solved the issue with “Swipeable” component for us

Using key prop will lose all the benefits of using it over FlatList https://shopify.github.io/flash-list/docs/fundamentals/performant-components#remove-key-prop

We have the same issue. When the screen goes out of sight while scrolling and comes back. The avatar Image source back to load defaultSource imageUri

<FlashList
    data={displayList}
    keyExtractor={item => item.service_list_id}
    renderItem={({ item }) => renderFlatListItem(item)}
    onEndReachedThreshold={0.5}
    onEndReached={onScrollLoad}
    estimatedItemSize={103}
  />

the avatar image in renderItem

<Image
    style={styles.avatarCircle}
    onError={() => setImageError(true)}
    source={
      imageError
        ? require('@/assets/images/components/OakMegaAvatar/avatar-default.png')
        : {
            uri: imageUri
          }
    }
    defaultSource={require('@/assets/images/components/OakMegaAvatar/avatar-default.png')}
  />

https://github.com/Shopify/flash-list/assets/61536315/ea943e24-bf9e-4257-847f-1890a0684ea2

Thanks for your feedback, I will try that. Does your component use unitId somewhere in the render? Our component is stateless so I wondering 😦 But I guess it’s possible to pass one of the props through state to achieve it.

Yeah, glad to help!

My component uses unitId as the state, I have bottomSheet inside to select units and use unitId to determine active item.

Adding “key={someId}” prop to the rendered item solved the issue with “Swipeable” component for us

Using key prop will lose all the benefits of using it over FlatList https://shopify.github.io/flash-list/docs/fundamentals/performant-components#remove-key-prop

Indeed. So it seems there is no solution for “Swipable” without loosing performance?

No, I think you can store swiped items in the list, and then apply them to item using https://shopify.github.io/flash-list/docs/recycling

this is my code

renderItem: renderItem={({ item }) => { const product = products?.find?.((product) => product.id === item.id) return ( <ProductItem data={item} active={!!product} selectedUnitId={product?.variantUnitId} onSelect={(data, onError) => ChatStore.selectProduct(data, onError)} /> ) }}

inside item component: const lastItemId = useRef(data.id) const [unitId, setUnitId] = useState<number>(selectedUnitId || data.units?.[0]?.id) const price = unitId ? data?.units?.find((item) => item.id === unitId)?.price : data?.price

if (lastItemId.current !== data.id) { if (selectedUnitId) { setUnitId(selectedUnitId) } else if (!unitId && data?.units?.length) { setUnitId(data.units[0]?.id) } lastItemId.current = data.id }