callstack / react-native-pager-view

React Native wrapper for the Android ViewPager and iOS UIPageViewController.
MIT License
2.66k stars 411 forks source link

☂️ Pager View Improvements #673

Open troZee opened 1 year ago

troZee commented 1 year ago

Intro

It is been a while since I am maintaining the react-native-pager-view. Previously it was part of the RN core, but then it has been extracted into a separate package. It has used 2 versions of ViewPager (v1, v2) and UIPageView. I tried to fix all issues, but some of them required some workarounds that were not stable enough. Those workarounds created other issues, hence other workarounds were used to fix that. To be honest, I was a little bit exhausted from it, hence together with @krozniata and @okwasniewski decided to build those components from the scratch

Next branch

@alpha0010 did a great job by implementing the LazyViewPager component. We released his changes into the next branch and we had the plan to merge it into the main branch. But we did some internal tests on real applications and in some cases, it did not work well. Internally we used another workaround to achieve the lazy effect. Moreover, we did not want to add additional complexity, when it did not bring more benefits and would add additional bugs to fix. With new architecture implementing a lazy approach should be much easier. Unfortunately, we won’t merge the next branch into the main. We will implement a lazy approach using a new architecture. If you would like to maintain LazyViewPager, feel free to make a fork

New architecture support

Meta has released a new architecture called fabric. In this library, we would like to focus first on the new architecture. After that, we will evaluate, if it would be worth migrating a new solution to the old arch (Right now it is hard to estimate it based on a priori knowledge)

Motivation

We would like to have more control over native components to easily fix issues or add new features. Right now, those components are black boxes, which are hard to maintain. Moreover, UIPageViewController is not maintained anymore and is not used in the iOS world. Unfortunately, we cannot use UICollectionView, because it does not work well in the RN world.

The same thing refers RecyclerView component for Android. The currently used ViewPager2 component on Android brought more issues than benefits since it uses the earlier-mentioned RecyclerView.

troZee commented 1 year ago

Here you can see a draft implementation on an iOS platform: https://github.com/callstack/react-native-pager-view/pull/672

erkie commented 1 year ago

What's the recommended way to implement lazy loading screens with the latest releases? I can't find any documentation, yet you mention that it's easier now? LazyPagerView was working great for us.

alexeykomov commented 1 year ago

@troZee Hello, thanks! What's the complexity with UICollectionView? Is it the fact that reactSubviews expose subviews of view, but there's no such equivalent of cells for a UICollectionView?

Also, side question - is React Native's FlatList implemented with plain UIScrollView, not UICollectionView?

Thanks.

r1me75 commented 1 year ago

What's the recommended way to implement lazy loading screens with the latest releases? I can't find any documentation, yet you mention that it's easier now? LazyPagerView was working great for us.

Same, do you have an update on this?

erkie commented 1 year ago

What's the recommended way to implement lazy loading screens with the latest releases? I can't find any documentation, yet you mention that it's easier now? LazyPagerView was working great for us.

Same, do you have an update on this?

We switched to just using the default import PagerView from "react-native-pager-view"; and implementing a simple lazy load logic in the sub view:

// Using pager

<PagerView
  onPageSelected={onPageSelected}
>
  {things.map((thing, index) => (
    <LazyLoadView
      key={thing.id}
      item={thing}
      index={index}
      currentIndex={currentNavigation.currentIndex}
    />
  ))}
</PagerView>

// LazyLoadView

const LazyLoadView = ({ item, currentIndex, index }) => {
  const isActive = indexIsActive(currentIndex, index);
  if (!isActive) {
    return <View key={item.id}></View>
  }

  // Expensive render full
  return // ...
}

export function indexIsActive(currentIndex, myIndex) {
  return currentIndex == myIndex ||
    currentIndex-1 == myIndex ||
    currentIndex+1 == myIndex;
}

So far the performance is equal.

mweel1 commented 11 months ago

@erkie what is

currentIndex={currentNavigation.currentIndex}

erkie commented 11 months ago

@mweel1 it's just a memoised object that I can pass around with details about the navigation state:

  const currentNavigation = useMemo(() => {
    return {
      currentIndex,
      totalLength: posts.length
    }
  }, [currentIndex, posts.length]);

You can of course just use a currentIndex variable instead.

quicksilverr commented 10 months ago

Is this going to be a part of the improvements? https://github.com/callstack/react-native-pager-view/issues/321

hubciorz commented 9 months ago

How does it relate to #173? We received an email from Google Play yesterday stating that the number of crashes on one of the devices is too high, which may result in limited visibility of our app in the store.

dareman-venkat commented 5 months ago
{things.map((thing, index) => (
    <LazyLoadView
      key={thing.id}
      item={thing}
      index={index}
      currentIndex={currentNavigation.currentIndex}
    />
  ))}

Could you please share the OnpageSelected Function and how did you calculated the curetindex and in my useCase i dont want to render all the screens since it has api calls involved and could you share the detailed code example with example screns integrated so it would be helpfull

chj-damon commented 5 months ago

I noticed that react-native-tab-view has a lazy prop. https://reactnavigation.org/docs/tab-view/#use-lazy-and-renderlazyplaceholder-props-to-render-routes-as-needed

but I think it would be nice if PagerView can do this automatically.

SeanRobinson159 commented 5 months ago

Moreover, UIPageViewController is not maintained anymore and is not used in the iOS world.

As an iOS developer in the iOS world, I don't understand this. What do you mean it is not maintained? Looking at Apple's documentation it is not marked as deprecated. Moving away from UIPageViewController will lose the built-in lazy loading and built in page indicators.

IronHeartDan commented 3 months ago

I made myself this wrapper and it works pretty good. Hope it is useful to someone

import React, { useEffect, useState } from 'react';

const LazyComponent = ({ componentKey, currentKey, component, placeholder }) => {
    const [hasRendered, setHasRendered] = useState(false);

    useEffect(() => {
        if (!hasRendered && currentKey === componentKey)
            setHasRendered(true);
    }, [currentKey, componentKey, hasRendered]);

    if (hasRendered) return component;
    return placeholder || <></>;
};

export default LazyComponent;