Shopify / flash-list

A better list for React Native
https://shopify.github.io/flash-list/
MIT License
5.61k stars 283 forks source link

item order is changing after scroll #745

Closed hotaryuzaki closed 1 year ago

hotaryuzaki commented 1 year ago

Current behavior

i have gallery with 3 columns, my script are below:

<FlashList
    keyExtractor={keyExtractor}
    numColumns={3}
    data={photos}
    renderItem={renderItem}
    ListFooterComponent={renderFlatListFooter}
    estimatedItemSize={200}
    onEndReached={_handleLoadMore}
    onEndReachedThreshold={0.3}
    onMomentumScrollBegin={() => onEndReachedCalledDuringMomentum = false}
    // drawDistance={Layout.window.height}
/>

and here is the renderItem, i create RenderItem component so it just rerender for checked item.

  const renderItem = useCallback(({ index, item }) => {
    console.log('renderItem index', index);
    const checkedIndex = imageChecked.findIndex((checkedItem) => checkedItem.id == item.id);

    return (
      <RenderItem
        index={index}
        item={item}
        onPress={_onPressCheckbox}
        selectedAlbum={selectedAlbum}
        checkedIndex={checkedIndex != -1 ? true : false}
      />
    );

  }, [photos, imageChecked]);

here is RenderItem component

const RenderItem = memo(({index, item, onPress, selectedAlbum, checkedIndex}) => {
  const [checked, setChecked] = useState(checkedIndex);

  useEffect(() => {
    if (checkedIndex != checked) onPress({ index, ...item, setChecked });
  }, [checked]);

  useEffect(() => {
    if (checkedIndex != checked) setChecked(checkedIndex);
  }, [checkedIndex]);

  return (
    <View key={index} style={styles.imageContainerStyle}>
      <Pressable
        key={index}
        onPress={() => setChecked(!checked)}
        style={styles.imageBoxStyle}
        android_ripple={{ color: 'rgba(245, 245, 245, 0.2)', foreground: true }}
      >
        <>
          {
            item.uri
              ? (
                <FastImage
                  source={{ uri: item.uri }}
                  resizeMode={FastImage.resizeMode.cover}
                  style={[
                    styles.imageStyle,
                    checked && { opacity: 0.3 },
                  ]}
                />
              )

              : (
                <View
                  style={{
                    ...styles.imageBoxStyle,
                    backgroundColor: Colors.colorLevel4,
                  }}
                >
                  <Icon.MaterialCommunityIcons
                    name="folder-image"
                    size={50}
                    color={Colors.colorLevel2}
                  />

                  <View style={styles.iconStyle}>
                    <OpenSansMedium
                      style={styles.iconStyleText}
                      numberOfLines={2}
                    >
                      Album:
                      {"\n" + selectedAlbum.title}
                    </OpenSansMedium>
                  </View>
                </View>
              )
          }

          {
            index > 0 &&
              <View style={styles.imageCheckboxContainer}>
                <Checkbox
                  status={checked ? "checked" : "unchecked"}
                  color="rgba(30, 26, 29, 0.38)"
                  onPress={() => setChecked(!checked)}
                  accessibilityLabel={"label_" + index}
                />
                <Text style={{ fontSize: 40 }}>{index}</Text>
              </View>
          }
        </>
      </Pressable>
    </View>
  )

}, function areEqual(prevProps, nextProps) {
  /*
  return true if passing nextProps to render would return
  the same result as passing prevProps to render,
  otherwise return false
  */
  if (prevProps.checkedIndex !== nextProps.checkedIndex) {
    return false;
  }

  return true;
});

the problem is when i scroll down the gallery and scroll back to the top the order of the images is changing. i see a blank space then it show the incorrect order, look the number inside the item image. when i create console.log in renderItem, it always calls in every scroll. it's like rerender, maybe that is why i look a blank screen

here is the gallery before i scroll down

drawing

and here is the gallery after i scroll down and scroll back to the top:

drawing

Expected behavior

I changed to the standard FlatList by React Native and there is no issue. even after pagination called the images gallery still in order. so my conclusion this issue is cause by FlashList

To Reproduce

maybe you can copy paste my script above

Platform:

Android

Environment

1.4.0

hotaryuzaki commented 1 year ago

the solution is i have to add key manually in my RenderItem

<RenderItem
        key={`${index}-${item.id}`} // ADD THIS
        index={index}
        item={item}
        onPress={_onPressCheckbox}
        selectedAlbum={selectedAlbum}
        checkedIndex={checkedIndex != -1 ? true : false}
      />
mickdewald commented 1 year ago

This does help indeed, but now I got plenty of blank spaces when scrolling (not even fast scrolling). This is even worse than using FlatList or VirtualizedList

dangnguyen1004 commented 6 months ago

the solution is i have to add key manually in my RenderItem

<RenderItem
        key={`${index}-${item.id}`} // ADD THIS
        index={index}
        item={item}
        onPress={_onPressCheckbox}
        selectedAlbum={selectedAlbum}
        checkedIndex={checkedIndex != -1 ? true : false}
      />

Wow, really did work for me, thank you