react-native-swiper-flatlist icon indicating copy to clipboard operation
react-native-swiper-flatlist copied to clipboard

onChangeIndex() doesn't always fire when using swipe feature

Open curtismenmuir opened this issue 3 years ago • 28 comments

Description

  • When user manually swipes item to side numerous times, onChangeIndex callback occasionally is not fired.
  • I have stripped back the props I was passing to <SwiperFlatList> to the bare minimum (see code example below) and issue still seen.
  • Issue only seen with manual swipe

Info

  • Issue seen on Android + iOS
  • RN Version: 0.63.4 (also seen in v0.62.2)
  • react-native-swiper-flatlist version: 3.0.14 (also seen in v3.0.8)

Code Example:

const LIST_ITEMS = [
  { title: 'Title 1 },
  { title: 'Title 2' },
  { title: 'Title 3' },
  { title: 'Title 4' },
];

export default class SwipeView extends Component {
  constructor(props) {
      super(props);
    }

  onChangeIndex({ index, prevIndex }) {
      console.log('New Index: ' + index);
    }
  
  renderItem = ({ item }) => {
      return <SwipeListItem item={item} />;
    };
  
  render() {
    return (
      <SwiperFlatList
        autoplay={false}
        data={LIST_ITEMS}
        renderItem={this.renderItem}
        showPagination={false}
        onChangeIndex={this.onChangeIndex.bind(this)}
      />
    )
  }
}

Steps to recreate

  • Render list with 4 items
  • Swipe forward to index 1
  • Swipe back to index 0
  • Repeat swiping between index 0 & 1 many times
  • onChangeIndex callback not always fired

curtismenmuir avatar Mar 19 '21 21:03 curtismenmuir

I have found a work-around by also implementing onMomentumScrollEnd callback. Leaving this issue open incase you want to track the onChangeIndex issue.

curtismenmuir avatar Mar 19 '21 22:03 curtismenmuir

Done a bit of digging into onChangeIndex issue and it looks like _onChangeIndex() in SwiperFlatList.tsx (line 77) receives the same value for _index and _prevIndex and this causes the onChangeIndex callback not to fire.

From a quick look, it seems like _onViewableItemsChanged() (line 181) is not setting the index on the swipe previous to the swipe which does not fire onChangeIndex callback. EG:

if (newItem.isViewable) {
  setCurrentIndexes((prevState) => ({ ...prevState, index: nextIndex }));
}

I just had a quick look, I will try do some more investigation when I have some free time.

curtismenmuir avatar Mar 20 '21 01:03 curtismenmuir

@curtismenmuir thanks for reporting the issue and the work-around, if you could take a deep look at it, it would be great 🙌

gusgard avatar Apr 01 '21 00:04 gusgard

@curtismenmuir thanks for reporting the issue and the work-around, if you could take a deep look at it, it would be great 🙌

gusgard avatar Apr 01 '21 00:04 gusgard

I also faced this issue. I noticed paginationIndex props of PaginationComponent always gives correct index. I found the value can be derived from parameters of onViewableItemsChanged. So here is my current workaround:

 onViewableItemsChanged={(params) => console.log('index', params.changed?.[0]?.index)}

PaperMonster avatar Apr 08 '21 07:04 PaperMonster

Same issue persist for me also. I have noticed that onchange index trigger after some delay. Any luck ?

arcsoftanil avatar Jun 02 '21 12:06 arcsoftanil

I'm facing this issue , only when I swipe manually Screenshot_2022-06-03-12-43-05-61_4007cdf3d38763fe5bc19f163cbad3b9__01 Screenshot_2022-06-03-12-43-02-16_4007cdf3d38763fe5bc19f163cbad3b9__01

Priyadharshini-max avatar Jun 03 '22 07:06 Priyadharshini-max

It show half image, I need to show like this when i swipe manuall Screenshot_2022-06-03-12-46-57-04_4007cdf3d38763fe5bc19f163cbad3b9__01 y

Priyadharshini-max avatar Jun 03 '22 07:06 Priyadharshini-max

Same issue persist for me also. I have noticed that onchange index trigger after some delay. Any luck ?

I'm facing the same delay issue... :(

iortega-dev avatar Jul 20 '22 17:07 iortega-dev

+1 experiencing this issue!

zachnicoll avatar Sep 11 '22 22:09 zachnicoll

+1 having the same issue when swipe manually.

ShobanaBohs100 avatar Sep 20 '22 12:09 ShobanaBohs100

figured out onMomentumScrollEnd solves the problem for now. Thanks @curtismenmuir

Instead this, onChangeIndex={({ index }) => { setListIndex(index); }} we can use this prop to get the index onMomentumScrollEnd={({ index }: any) => setListIndex(index)}

ShobanaBohs100 avatar Sep 20 '22 12:09 ShobanaBohs100

onMomentumScrollEnd doesn't work on Android

garrettg123 avatar Nov 18 '22 08:11 garrettg123

+1 having issues where onChangeIndex doesn't update the index.

apethree avatar Feb 22 '23 18:02 apethree

@ShobanaBohs100 onMomentumScrollEnd={({ index }: any) => setListIndex(index)} Thanks!!! this perfectly worked for IOS. but android still having same issue. What did you use for android?

varunkukade avatar Mar 08 '23 10:03 varunkukade

+1

DanielRiera avatar Apr 19 '23 16:04 DanielRiera

I did a few improvements (removing re-renders and adding support for getItemLayout), please use version 3.2.2 and let me know. Thanks

gusgard avatar Apr 23 '23 09:04 gusgard

@gusgard Still the same issue on 3.2.2 :(

bravecode avatar Apr 27 '23 01:04 bravecode

I wasn't able to reproduce it with Expo v48, is there an example or steps you can provide me ? @bravecode

gusgard avatar Apr 27 '23 03:04 gusgard

i achieve this design and also created this code blog. https://buzzstuck.com/react-native-swiper-component-implemented-with-flatlist/

kishanvaghera avatar May 14 '23 12:05 kishanvaghera

/node_modules/react-native-swiper-flatlist/src/components/SwiperFlatList/SwiperFlatList.tsx      
 
     const _onChangeIndex = React.useCallback(
       ({ index: _index, prevIndex: _prevIndex }: { index: number; prevIndex: number }) => {
       	
         // -> Moving it out of the condition can temporarily solve the issue. Another problem is that _onChangeIndex cannot be triggered immediately after the swipe, but needs to wait for a moment.
        onChangeIndex?.({ index: _index, prevIndex: _prevIndex });

         //  -> First swipe right, then swipe left will not trigger _onChangeIndex.
         if (_index !== _prevIndex) {
          onChangeIndex?.({ index: _index, prevIndex: _prevIndex });
         // onChangeIndex?.({ index: _index, prevIndex: _prevIndex });
         }
       },
       [onChangeIndex]

Riuhou avatar Jun 14 '23 03:06 Riuhou

+1

I have 6 items onChnageIndex is not firing after 3rd Index.

  const renderItem = ({item, index}: {item: IHome; index: number}) => {
    return (
      <View height={height * 0.9} width={width}>
        <Video
          source={{
            uri: item.attachmentUrl,
          }}
          paused={currentIndex !== index}
          style={styles.video}
          resizeMode="stretch"
          // controls
          onLoad={() => setLoading(false)}
          onLoadStart={() => setLoading(true)}
          onError={() => setLoading(false)}
          repeat
        />
        <View position={'absolute'} bottom={height * 0.1} right={5}>
          <Pressable>
            {item.is_Liked ? (
              <SelectedLike height={7} width={7} />
            ) : (
              <Likes height={7} width={7} />
            )}
            <Text
              style={[
                styles.text,
                styles.count,
                theme === 'light' ? styles.countLight : styles.countDark,
              ]}>
              {formatCount(item.total_likes)}
            </Text>
          </Pressable>
          <Pressable mt={5}>
            <MessageGrey height={7} width={7} />
            <Text
              style={[
                styles.text,
                styles.count,
                theme === 'light' ? styles.countLight : styles.countDark,
              ]}>
              {formatCount(item.total_comments)}
            </Text>
          </Pressable>
          <Pressable mt={5}>
            <FavoritesWhite height={7} width={7} />
          </Pressable>
        </View>
        <View
          position={'absolute'}
          bottom={height * 0.1}
          left={5}
          width={widthToDp(65)}>
          <HStack>
            <FastImage
              style={styles.profilePic}
              source={{uri: item.profile_pic}}
            />
            <View ml={3}>
              <Text
                style={[
                  styles.text,
                  styles.fullName,
                  theme === 'light' ? styles.countLight : styles.countDark,
                ]}>
                {item.full_name}
              </Text>
              <Text
                style={[
                  styles.text,
                  styles.bio,
                  theme === 'light' ? styles.bioLight : styles.bioDark,
                ]}>
                {item.user_bio}
              </Text>
            </View>
          </HStack>
          <Text
            style={[
              styles.text,
              styles.bio,
              theme === 'light' ? styles.countLight : styles.countDark,
            ]}>
            {item.description}
          </Text>
        </View>
      </View>
    );
  };
  
<SwiperFlatList
            vertical
            ref={flatListRef}
            data={reels}
            autoplay={false}
            
            renderItem={renderItem}
            onChangeIndex={({index, prevIndex}) => {
              console.log('prev=>', prevIndex, 'current index=>', index);
              setCurrentIndex(index);
            }}
            keyExtractor={item => item.id}
          />
          

iamsaadMehmood avatar Jun 19 '23 05:06 iamsaadMehmood

If any one have any other work around for getting the index of current visible item then please let me know

dprajapati1179 avatar Jun 30 '23 07:06 dprajapati1179

I did a few improvements (removing re-renders and adding support for getItemLayout), please use version 3.2.2 and let me know. Thanks

This worked for me! I was having this issue with onChangeIndex function, it would reach the third item in the array and stop counting. I'm using the list vertically and have a fixed header at the top with a height of 90px. I believe in this part of the code

if (props.getItemLayout === undefined) {
      const itemDimension = vertical ? height : width;
      flatListProps.getItemLayout = (__data, ItemIndex: number) => ({
        length: itemDimension,
        offset: itemDimension * ItemIndex,
        index: ItemIndex,
      });
    }

the offset didn't match the height of the images in the list. I just needed to provide this information in the SwiperFlatList:

<SwiperFlatList
        data={content}
        renderItem={({index, item}) => renderItem({index, item})}
        vertical
        keyExtractor={item => item.itemId.toString()}
        getItemLayout={(_d, i) => ({
          length: Dimensions.get('window').height,
          index: i,
          offset: (Dimensions.get('window').height - 90) * i,
        })}
        onChangeIndex={({index, prevIndex}) => console.log(index)}
      />

Now the onChangeIndex function is correctly returning the visible index. I'm not sure if it applies to everyone here, but it worked for my case.

douglas-esportudo avatar Jul 11 '23 00:07 douglas-esportudo

I can confirm that setting getItemLayout with correct dimensions triggers the onChangeIndex correctly, can it be added to the docs?

devakrishna33 avatar Oct 11 '23 00:10 devakrishna33

hey @devakrishna33 could you kindly share your code snippet?

lsps9150414 avatar Oct 20 '23 01:10 lsps9150414

same

BLOCKMATERIAL avatar Mar 12 '24 11:03 BLOCKMATERIAL

Should be fixed in 3.2.4

gusgard avatar Apr 23 '24 03:04 gusgard