Incorrect pages when content changes dynamically [iOS]
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
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 the key workaround solved my problem.
Same for me, the key workaround solved the problem, but without the workaround the issue still exist in 6.2.0.
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));
}
}}>
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}