hoaphantn7604 / react-native-element-dropdown

A react-native dropdown component easy to customize for both iOS and Android.
MIT License
942 stars 163 forks source link

Dropdown list flickering on open #198

Open daankennes opened 1 year ago

daankennes commented 1 year ago

Hi!

Firstly, thanks for your work on this project! 🙂

EDIT: this only seems to happen on Android

Upon opening the dropdown, the position measurement is wrong and is then recalculated. However, the wrong position is used for a first render which makes the list slightly out of position for a split second (see image). image

I solved the problem by waiting for the recalculation, but it is not optimal and I wonder why the initial position is wrong in the first place.

Here is the diff that solved my problem:

diff --git a/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx b/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
index 83981dc..708b502 100644
--- a/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
+++ b/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
@@ -17,13 +17,13 @@ import {
   Keyboard,
   KeyboardEvent,
   Modal,
+  StatusBar,
   StyleSheet,
   Text,
   TouchableHighlight,
   TouchableWithoutFeedback,
   View,
   ViewStyle,
-  StatusBar,
 } from 'react-native';
 import { useDetectDevice } from '../../toolkits';
 import { useDeviceOrientation } from '../../useDeviceOrientation';
@@ -98,6 +98,7 @@ const DropdownComponent: <T>(
     const [position, setPosition] = useState<any>();
     const [keyboardHeight, setKeyboardHeight] = useState<number>(0);
     const [searchText, setSearchText] = useState('');
+    const [onOpenPositionCalculated, setOnOpenPositionCalculated] = useState<boolean>(false);

     const { width: W, height: H } = Dimensions.get('window');
     const styleContainerVertical: ViewStyle = useMemo(() => {
@@ -158,7 +159,7 @@ const DropdownComponent: <T>(
       }
     }, [fontFamily]);

-    const _measure = useCallback(() => {
+    const _measure = useCallback((calledFrom?) => {
       if (ref && ref?.current) {
         ref.current.measureInWindow((pageX, pageY, width, height) => {
           const isFull = isTablet
@@ -176,9 +177,12 @@ const DropdownComponent: <T>(
             left: Math.floor(left),
             height: Math.floor(height),
           });
+          if (calledFrom === 'showOrClose' && !onOpenPositionCalculated) {
+            setOnOpenPositionCalculated(true);
+          }
         });
       }
-    }, [H, W, orientation, mode]);
+    }, [H, W, orientation, mode, onOpenPositionCalculated]);

     const onKeyboardDidShow = useCallback(
       (e: KeyboardEvent) => {
@@ -260,9 +264,9 @@ const DropdownComponent: <T>(
           return Keyboard.dismiss();
         }

-        _measure();
         setVisible(!visible);
         setListData(data);
+        _measure('showOrClose');

         if (!visible) {
           if (onFocus) {
@@ -546,7 +550,7 @@ const DropdownComponent: <T>(
     );

     const _renderModal = useCallback(() => {
-      if (visible && position) {
+      if (visible && position && onOpenPositionCalculated) {
         const { isFull, width, height, top, bottom, left } = position;

         const onAutoPosition = () => {

This issue body was partially generated by patch-package.

Zcehtro commented 1 year ago

hi @daankennes any chance you could submit this as a PR?

I'm developing an app with this component and I'm seeing a similar flickering.

It's just as you describe: it renders and then recalculates its position while still visible. It's very un-aesthetic.

daankennes commented 12 months ago

Hi @Zcehtro. I am currently on holiday. I can create a PR when I'm back (in a week). However, it is easy to apply the fix yourself or copy the diff and use patch-package.

kaladivo commented 7 months ago

Sadly the patch package snippet does no longer work. Does anyone have a fix for this?

vladanzlatar commented 6 months ago

I am using <Dropdown /> inside of screen that has animation(presentation: "modal" from react-navigation) and it looks like onLayout is being called at times Dropdown does not have correct position and it causes flickering when we open it for the first time, so by removing onLayout Dropdown position is calculated correctly and displayed only when we have correct position

Here is the diff that solved my problem:

diff --git a/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx b/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
index 07bf17b..3ebfd90 100644
--- a/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
+++ b/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
@@ -661,7 +661,6 @@ const DropdownComponent: <T>(
       <View
         style={StyleSheet.flatten([styles.mainWrap, style])}
         ref={ref}
-        onLayout={_measure}
       >
         {_renderDropdown()}
         {_renderModal()}

This issue body was partially generated by patch-package.

Djhitman commented 3 weeks ago

any updates about this problem?

Ao-chi commented 3 weeks ago

Having same issue, I tried this patch but it doesn't solve it. I'm using the v.2.12.1

I am using <Dropdown /> inside of screen that has animation(presentation: "modal" from react-navigation) and it looks like onLayout is being called at times Dropdown does not have correct position and it causes flickering when we open it for the first time, so by removing onLayout Dropdown position is calculated correctly and displayed only when we have correct position

Here is the diff that solved my problem:

diff --git a/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx b/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
index 07bf17b..3ebfd90 100644
--- a/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
+++ b/node_modules/react-native-element-dropdown/src/components/Dropdown/index.tsx
@@ -661,7 +661,6 @@ const DropdownComponent: <T>(
       <View
         style={StyleSheet.flatten([styles.mainWrap, style])}
         ref={ref}
-        onLayout={_measure}
       >
         {_renderDropdown()}
         {_renderModal()}

This issue body was partially generated by patch-package.