react-native-pager-view icon indicating copy to clipboard operation
react-native-pager-view copied to clipboard

Incorrect pages when content changes dynamically [iOS]

Open yuri-lomashko-itechart opened this issue 3 years ago • 5 comments

Environment

Platform: iOS react-native: 0.69.5 react-native-pager-view: 5.4.25

Description

I use the library for vertical scroll of fullscreen pages and receive few pages from BE. In cases when number of pages is changed I see the empty screens or not the whole list of pages. It's hard to say when it happens exactly, but in example bellow it can be reproduced in 80% cases. Also if to add some additional render then issue can be reproduced more frequent

Reproducible Demo

Video

https://user-images.githubusercontent.com/54143988/188158346-73388cf2-01b4-4ac7-a027-5e38c239c4f2.mov

Code sample

import { useState } from 'react';
import { Button, SafeAreaView, Text, View } from 'react-native';
import PagerView from 'react-native-pager-view';

const ids = ['first', 'second', 'third'];

function getNewArray() {
  return ids.reduce<string[]>((array, id) => {
    if (Math.round(Math.random())) {
      array.push(id);
    }
    return array;
  }, []);
}

export const Test = () => {
  const [, setCounter] = useState(0);
  const [pagesContent, setPagesContent] = useState<string[]>([]);

  console.log(pagesContent);

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <Text style={{ textAlign: 'center' }}>{pagesContent.length}</Text>
      <PagerView orientation={'vertical'} overdrag={false} style={{ flex: 1 }}>
        {pagesContent.map((content) => (
          <View key={content}>
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
              <Text style={{ fontSize: 30 }}>{content}</Text>
            </View>
          </View>
        ))}
      </PagerView>
      <Button
        title={'Set with random'}
        onPress={() => {
          setPagesContent(getNewArray());
          // to get more frequent effect, one of the next lines can be used
          // setCounter((prev) => prev + 1);
          // setImmediate(() => setCounter((prev) => prev + 1));
        }}
      />
    </SafeAreaView>
  );
};

Workaround

Just add key to PagerView like key={pagesContent.toString()} but it will produce re-render Pager itself and in some cases it's not possible to use this hack

yuri-lomashko-itechart avatar Sep 02 '22 13:09 yuri-lomashko-itechart

May be related - #518

It seems that I don't have current problem when I use solution from 27e8003 instead of 3260863

yuri-lomashko-itechart avatar Sep 22 '22 14:09 yuri-lomashko-itechart

@yuri-lomashko-itechart the key workaround solved my problem.

jwanga avatar Jan 11 '23 03:01 jwanga

Same for me, the key workaround solved the problem, but without the workaround the issue still exist in 6.2.0.

Melchek avatar Apr 04 '23 15:04 Melchek

Same problem.

It works by call setPageWithoutAnimation before changing pages in my project.

 onPageSelected={e => {
            // add this
            pageViewRef.current?.setPageWithoutAnimation(1);
            const { position } = e.nativeEvent;

            if (position === 2) {
              setPages(pages.map(item => item + 1));
            } else if (position === 0) {
              setPages(pages.map(item => item - 1));
            }
          }}>

yqz0203 avatar Apr 22 '23 08:04 yqz0203

I am experiencing similar issues neither of the NATIVE fixes @yuri-lomashko-itechart suggested worked for me.

In the case of my app, I have a quiz that a user interacts with if new Content DIRECTLY PROCEEDS this quiz (i.e it is loaded in based on user interaction in the next slide) it breaks / blocks navigation and is the wrong (stale / previous content)

I've narrowed it down in my case to

https://github.com/callstack/react-native-pager-view/blob/master/ios/ReactNativePageView.m#L217-L233

I changed the else block here to the below code and it works for my use case but not sure this is a viable solution for the library but I'd like to commit it if it is or someone more versed on Obj-C cares to help me refactor

else {
        // If this runs on load sometimes we get stuck in state where we can't scroll
        // my assumption is trying to go to an index which doesn't exist causes
        // an error and it just kinda silently breaks.
        if(newIndex != 0){
        [self goTo:newIndex  - 1 animated:NO];
        }
        if(newIndex == 0) {
        [self goTo:newIndex + 1 animated:NO];
        }
        [self goTo:newIndex animated:NO];
    }

Working state

https://github.com/callstack/react-native-pager-view/assets/25855018/06ebe165-b246-422d-9cc2-bffd62f742fd

Broken State

https://github.com/callstack/react-native-pager-view/assets/25855018/527206be-35a1-420a-b5f2-9f78e7f8f765

In my host app I am also setting animated page view's prop of scrollEnabled to false until my request resolves.

isStackedScrollingEnabled is set to true when request begins then false once request ends.

    <AnimatedPagerView
      scrollEnabled={Platform.OS === 'ios'  && typeof isStackedScrollingEnabled !== 'undefined' ? isStackedScrollingEnabled : true}

chrisjlan89 avatar Oct 26 '23 20:10 chrisjlan89