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

[@next] `useNativeDriver: true` in `onPageScroll` works with `PagerView`, but not `LazyPagerView`

Open SimpleCreations opened this issue 3 years ago • 5 comments

Description

When using Animated.event in onPageScroll of LazyPagerView and setting useNativeDriver to true, the animated values of position and offset do not get updated at all. The same setup works as intended with the regular PagerView. This leads to poor performance of animations based on the page position.

Reproducible Demo

Here's a code snippet which uses PagerView and where the animated values are properly updated:

import React, {useRef} from 'react';
import {Animated, SafeAreaView, StyleSheet, Text, View} from 'react-native';
import {PagerView} from 'react-native-pager-view';

const AnimatedPagerView = Animated.createAnimatedComponent(PagerView);

const App = () => {
  const position = useRef(new Animated.Value(0)).current;
  const offset = useRef(new Animated.Value(0)).current;
  const translateX = Animated.multiply(Animated.add(position, offset), 100);

  return (
    <SafeAreaView style={styles.container}>
      <Animated.View style={[styles.block, {transform: [{translateX}]}]} />
      <AnimatedPagerView
        style={styles.container}
        initialPage={0}
        onPageScroll={Animated.event([{nativeEvent: {position, offset}}], {
          useNativeDriver: true,
        })}>
        <View key="1" style={styles.page}>
          <Text>1</Text>
        </View>
        <View key="2" style={styles.page}>
          <Text>2</Text>
        </View>
        <View key="3" style={styles.page}>
          <Text>3</Text>
        </View>
      </AnimatedPagerView>
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {flex: 1},
  block: {marginBottom: 20, width: 30, height: 30, backgroundColor: '#000'},
  page: {borderWidth: 1},
});

export default App;

Here's the same setup which uses LazyPagerView and where the animated values are always at 0:

import React, {useRef} from 'react';
import {Animated, SafeAreaView, StyleSheet, Text, View} from 'react-native';
import {LazyPagerView} from 'react-native-pager-view';

const AnimatedLazyPagerView = Animated.createAnimatedComponent(LazyPagerView);

const App = () => {
  const position = useRef(new Animated.Value(0)).current;
  const offset = useRef(new Animated.Value(0)).current;
  const translateX = Animated.multiply(Animated.add(position, offset), 100);

  const data = [1, 2, 3];
  const renderItem = ({item}) => (
    <View key={item} style={styles.page}>
      <Text>{item}</Text>
    </View>
  );
  const extractKey = item => item + '';

  return (
    <SafeAreaView style={styles.container}>
      <Animated.View style={[styles.block, {transform: [{translateX}]}]} />
      <AnimatedLazyPagerView
        style={styles.container}
        initialPage={0}
        onPageScroll={Animated.event([{nativeEvent: {position, offset}}], {
          useNativeDriver: true,
        })}
        data={data}
        renderItem={renderItem}
        keyExtractor={extractKey}
      />
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
  container: {flex: 1},
  block: {marginBottom: 20, width: 30, height: 30, backgroundColor: '#000'},
  page: {borderWidth: 1},
});

export default App;

Changing useNativeDriver: true to useNativeDriver: false restores the functionality, but degrades performance.

SimpleCreations avatar Jun 14 '22 22:06 SimpleCreations

I've noticed that removing the wrapper around LazyPagerViewImpl and exporting LazyPagerViewImpl instead restores the useNativeDriver functionality. https://github.com/callstack/react-native-pager-view/blob/next/src/LazyPagerView.tsx#L33

Is there any reason this wrapper was added? I could make a PR removing it.

SimpleCreations avatar Jun 15 '22 10:06 SimpleCreations

Is there any reason this wrapper was added? I could make a PR removing it.

@alpha0010 Could you help me with above question ?

troZee avatar Jun 15 '22 15:06 troZee

The wrapper dramatically reduced calls to the main render() function, which (depending on data, config, etc.) is heavy. If you do remove the wrapper, I recommend caching the renderChildren() call (for example, with memoize-one).

alpha0010 avatar Jun 16 '22 03:06 alpha0010

@alpha0010 then I believe declaring LazyPagerViewImpl as PureComponent should have the same effect without the need for a wrapper or manual memoization. Or is there something I'm missing?

SimpleCreations avatar Jun 16 '22 12:06 SimpleCreations

Unfortunately, it has been too long since I worked on this for me to recall if I: did not test that, tested but it did not work, or maybe just implemented it wrong while testing (or maybe something else).

alpha0010 avatar Jun 16 '22 12:06 alpha0010

https://github.com/callstack/react-native-pager-view/issues/673

troZee avatar Dec 20 '22 17:12 troZee