callstack / react-native-pager-view

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

Blink initial page was solved by 5.4.0, but the initial page is not rendered on Android 6.0 #409

Open JackClown opened 3 years ago

JackClown commented 3 years ago

Environment

react native 0.63.4 Android 6.0.0 API 23 react-native-pager-view 5.4.0

Description

I upgrade the package from 5.2.4 to 5.4.0, because of the blink initial page. It works well on my phone - Android 10. But when I use my APP on the Android 6.0 device, the initial page is not rendered. After I scroll the page, the component works well.

Reproducible Demo

I think normal usage could reproduce the problem

function Demo(props) {
   return <PagerView initialPage={0} style={{ height: 100 }}>{props.children}</PagerView>
}

screencast-Genymotion-2021-07-28_09 18 42 043

JackClown commented 3 years ago

Finally I solve it by forcing refresh the native view in comopnentDidMount. Here is my patch:

diff --git a/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt b/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt
index 6e2a436..11cf7fe 100644
--- a/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt
+++ b/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/PagerViewViewManager.kt
@@ -186,7 +186,9 @@ class PagerViewViewManager : ViewGroupManager<ViewPager2>() {
       "setPageWithoutAnimation",
       COMMAND_SET_PAGE_WITHOUT_ANIMATION,
       "setScrollEnabled",
-      COMMAND_SET_SCROLL_ENABLED)
+      COMMAND_SET_SCROLL_ENABLED,
+      "forceRefresh",
+      COMMAND_FORCE_REFRESH)
   }

   override fun receiveCommand(root: ViewPager2, commandId: Int, args: ReadableArray?) {
@@ -208,6 +210,9 @@ class PagerViewViewManager : ViewGroupManager<ViewPager2>() {
       COMMAND_SET_SCROLL_ENABLED -> {
         root.isUserInputEnabled = args!!.getBoolean(0)
       }
+      COMMAND_FORCE_REFRESH -> {
+        (root.adapter as ViewPagerAdapter?)?.forceRefreshViews()
+      }
       else -> throw IllegalArgumentException(String.format(
         "Unsupported command %d received by %s.",
         commandId,
@@ -246,6 +251,7 @@ class PagerViewViewManager : ViewGroupManager<ViewPager2>() {
     private const val COMMAND_SET_PAGE = 1
     private const val COMMAND_SET_PAGE_WITHOUT_ANIMATION = 2
     private const val COMMAND_SET_SCROLL_ENABLED = 3
+    private const val COMMAND_FORCE_REFRESH = 4
   }
 }

diff --git a/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/ViewPagerAdapter.kt b/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/ViewPagerAdapter.kt
index 1f6b863..26317db 100644
--- a/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/ViewPagerAdapter.kt
+++ b/node_modules/react-native-pager-view/android/src/main/java/com/reactnativepagerview/ViewPagerAdapter.kt
@@ -33,6 +33,16 @@ class ViewPagerAdapter() : Adapter<ViewPagerViewHolder>() {
     return childrenViews.size
   }

+  fun forceRefreshViews() {
+    var mChildrenViews: ArrayList<View> = ArrayList();
+    mChildrenViews.addAll(childrenViews.toList());
+
+    childrenViews.clear();
+    childrenViews.addAll(mChildrenViews);
+
+    notifyDataSetChanged();
+  }
+
   fun addChild(child: View, index: Int) {
     childrenViews.add(index, child)
     notifyItemInserted(index)
diff --git a/node_modules/react-native-pager-view/src/PagerView.tsx b/node_modules/react-native-pager-view/src/PagerView.tsx
index f7585d5..f6040a2 100644
--- a/node_modules/react-native-pager-view/src/PagerView.tsx
+++ b/node_modules/react-native-pager-view/src/PagerView.tsx
@@ -140,6 +140,16 @@ export class PagerView extends React.Component<PagerViewProps> {
     }
   }

+  public componentDidMount() {
+    if (Platform.OS === 'android') {
+      UIManager.dispatchViewManagerCommand(
+        ReactNative.findNodeHandle(this),
+        getViewManagerConfig().Commands.forceRefresh,
+        []
+      );
+    }
+  }
+
   render() {
     return (
       <PagerViewViewManager

It is not a good way, but it works

troZee commented 3 years ago

Hey @JackClown Thank you for reporting an issue and sharing the solution. Could you create a PR for it ? I would like to take a look on it.

JackClown commented 3 years ago

Of course @troZee But I find another problem.When initialPage is not 0, the onPageSelect event could be triggered with position 0 at the first time.it doesn't come out every time. Here is my debug log on android:

07-28 17:54:18.157 6331-6331/com.nhsoft.shopapp3 D/pager initialPage: 2
07-28 17:54:18.157 6331-6331/com.nhsoft.shopapp3 D/pager onPageSelected: 2
07-28 17:54:24.174 6331-6331/com.nhsoft.shopapp3 D/pager initialPage: 2
07-28 17:54:24.174 6331-6331/com.nhsoft.shopapp3 D/pager onPageSelected: 2
07-28 17:54:28.933 6331-6331/com.nhsoft.shopapp3 D/pager initialPage: 2
07-28 17:54:28.971 6331-6331/com.nhsoft.shopapp3 D/pager onPageSelected: 0

And where I add the log

        override fun onPageSelected(position: Int) {
          super.onPageSelected(position)
          Log.d("pager onPageSelected", position.toString());
          eventDispatcher.dispatchEvent(
                  PageSelectedEvent(vp.id, position))
        }
@ReactProp(name = "initialPage", defaultInt = 0)
  fun setInitialPage(viewPager: ViewPager2, value: Int) {
    viewPager.post {
      Log.d("pager initialPage", value.toString());
      setCurrentItem(viewPager, value, false)
    }
  }

I don't apply the patch and use it in the react-navigation screen