Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v4] | [v2] BottomSheetModal failing to present on iOS #1560

Closed
walterholohan opened this issue Oct 4, 2023 · 57 comments
Closed

[v4] | [v2] BottomSheetModal failing to present on iOS #1560

walterholohan opened this issue Oct 4, 2023 · 57 comments
Labels
bug Something isn't working no-issue-activity

Comments

@walterholohan
Copy link

Bug

We just recently updated RN to 0.72.5 and react-native-reanimated to 3.5.4 and released it to production over the weekend. And then we started to get reports from our users that the modals were failing to appear. I will attach some videos below but we were unable to reproduce it on a simulator or a real device however we have ~150,000 MAU so after 24 hours we had over 10 users who were affected.

We had users on iOS 16.x and iOS 17.x who were affected

To unblock our users I had to set animateOnMount to false which makes me believe the issue is with the opening animation of the modal and reanimated

Environment info

Library Version
@gorhom/bottom-sheet 4.4.7
react-native 0.72.5
react-native-reanimated 3.5.4
react-native-gesture-handler 2.13.1

Steps To Reproduce

  1. User tries to press on Link Activity and you can see the modal briefly flashes
  2. User tries to press on Referral Code and again the modal flashes and closes
RPReplay_Final1696360061.mov

Reproducible sample code

can attach an example of code if necessary but I was just following to docs for BottomSheetModal
@walterholohan walterholohan added the bug Something isn't working label Oct 4, 2023
@walterholohan walterholohan changed the title [v4] | [v2] Issue title [v4] | [v2] BottomSheetModal failing to present on iOS Oct 4, 2023
@SrAnthony
Copy link

Same happening to us, affecting at least 15 users so far.
We couldn't get a repro yet either.

Bottom sheet 4.5.1
React native 0.72.5
Reanimated 3.5.4

@LouisKraemer
Copy link

Same here, animateOnMount to false does fix the issue but really low number of users affected.

@walterholohan
Copy link
Author

Thanks @SrAnthony and @LouisKraemer good to hear that you are also experiencing the same issue for your users. I haven't been able to deepdive into the animation implementation but hopefully @gorhom can give some insight or potential avenue's we can explore for a fix

@karbone4
Copy link

karbone4 commented Oct 5, 2023

Same here, we are also using expo sdk 49. It also happens on android

@enchorb
Copy link

enchorb commented Oct 5, 2023

Same issue here, more and more users are starting to complain about this - also unable to reproduce for us on simulator or our testing devices. This is happening for our users on both v4 and v5.

@hatem-72
Copy link

hatem-72 commented Oct 6, 2023

Some of our users are affected as well.
I think I managed to reproduce it, at least on IOS: Activate the Reduce Motion accessibility setting in your Iphone, restart your app then bottomsheets are not opening anymore.

Reanimated recently added support for this setting -> https://docs.swmansion.com/react-native-reanimated/docs/guides/accessibility/

@walterholohan
Copy link
Author

wow great spot @hatem-72 , did you manage to create a patch for this lib?

@hatem-72
Copy link

hatem-72 commented Oct 6, 2023

For the moment, I've only disabled the opening animation for users with reduced motion :

import { useReducedMotion } from 'react-native-reanimated';
import { BottomSheetModal } from '@gorhom/bottom-sheet';

// ...

function MyComponent() {
    const reducedMotion = useReducedMotion();

    return <BottomSheetModal
        // ...
        animateOnMount={!reducedMotion}
    >
        // ...
    </BottomSheetModal>
}

@enchorb
Copy link

enchorb commented Oct 6, 2023

@hatem-72 great find! reached out to 2 users who were having this issue and they both had it enabled.

@walterholohan
Copy link
Author

Can we buy @hatem-72 a coffee?

@karbone4
Copy link

karbone4 commented Oct 6, 2023

Thanks @hatem-72 for the fix.

I have another bug, I can't dismiss the bottom modal 2 times. If I dismiss, open it again, I can't dismiss it. I found a workaround by overriding ref :

const reducedMotion = useReducedMotion();
useImperativeHandle(
    bottomModalRef,
    () => ({
        dismiss: () => {
            if (reducedMotion) {
                ref?.current?.snapToPosition(0);
            } else {
                ref?.current?.dismiss();
            }
        }
    }),
    [reducedMotion]
);

@walterholohan
Copy link
Author

@karbone4 I am unable to reproduce your bug. It works for me

@leymytel
Copy link

leymytel commented Oct 10, 2023

Hello everyone!

Setting animateOnMount to false causes many weird issues (can't dismiss the modal, sometimes you need to click twice to get it open) . Another temporary fix is to add this patch (thanks to @efstathiosntonas) to override reduced motion to false in reanimated 3.5.4:

diff --git a/node_modules/react-native-reanimated/src/reanimated2/PlatformChecker.ts b/node_modules/react-native-reanimated/src/reanimated2/PlatformChecker.ts
index 9b3fcb1..0111380 100644
--- a/node_modules/react-native-reanimated/src/reanimated2/PlatformChecker.ts
+++ b/node_modules/react-native-reanimated/src/reanimated2/PlatformChecker.ts
@@ -49,5 +49,5 @@ export function isReducedMotion() {
       ? // @ts-ignore Fallback if `window` is undefined.
         !window.matchMedia('(prefers-reduced-motion: no-preference)').matches
       : false
-    : (global as localGlobal)._REANIMATED_IS_REDUCED_MOTION ?? false;
+    : false;
 }

@nihilenz
Copy link

Also observed on a device running Android 10 with reduce motion option enabled

@levibuzolic
Copy link

levibuzolic commented Oct 23, 2023

Also hitting this issue, rather than disabling the reduce motion detection for Reanimated globally we went with conditionally supplying our own animation config only when required. There are still some minor quirks when dismissing the sheet, but it does at least open and work correctly:

const reduceMotionEnabled = useReducedMotion();
const overrideConfig = useBottomSheetSpringConfigs({
  damping: 500,
  stiffness: 1000,
  mass: 3,
  overshootClamping: true,
  restDisplacementThreshold: 10,
  restSpeedThreshold: 10,
  reduceMotion: ReduceMotion.Never,
});

return (
  <BottomSheetModal
    /*
    There's a bug in BottomSheet which means that sheets will fail to open when reduce motion is enabled. Manually
    providing an animation config with `reduceMotion: ReduceMotion.Never` fixes this, but it does introduce a bit
    of jank when dismissing the sheet. This is a tradeoff we're willing to make for the sake of sheets at least
    working when reduce motion is enabled.

    @see https://github.com/gorhom/react-native-bottom-sheet/issues/1560
    */
    animationConfigs={reduceMotionEnabled ? overrideConfig : undefined}
    // other config here...
  />
);

The animation config was lifted from

const ANIMATION_CONFIGS_IOS = {
damping: 500,
stiffness: 1000,
mass: 3,
overshootClamping: true,
restDisplacementThreshold: 10,
restSpeedThreshold: 10,
};
to keep thre behaviour the same as before, I noticed that there's a platform fork and on Android the animation using a timing config, so you could probably use that based on platform, but we were happy enough with the spring config for both platforms.

@Gyogle
Copy link

Gyogle commented Oct 27, 2023

In fact, I think this problem is fundamentally a problem with the changes in Reanimated that were made when the BottomSheet version was upgraded, although BottomSheet itself handles reduceOption in a way.

Simple problem solving can be done in the other comment1 and comment2 posted above.
But I think the fundamental solution is to allow Reanimated itself to control the relevant settings.

So I posted my opinion and the cause I identified on Reanimated.(software-mansion/react-native-reanimated#5314 (comment))
We ask for your interest. Let’s solve this problem related to accessibility together!

@andac-ozcan
Copy link

andac-ozcan commented Oct 30, 2023

As I see, reduced motion is also enabled if low power mode is active and device battery is <= 10%. Almost all screenshots from our user reports shows that their device battery are <= 10%. Apple mentions this here, under Low Power Mode title. So it may be a wider issue than we think, not related with accessibility settings only.

@nguyenhoanganhdev
Copy link

nguyenhoanganhdev commented Nov 1, 2023

I think, reduced motion is also enabled if low power mode is active and device battery is <= 10%. Almost all screenshots from our user bug reports shows their device battery is <= 10%. Apple mentions this here, under Low Power Mode title. So it may be a wider issue than we think, not related with accessibility settings only.

Thank you very much. This is the root cause of my case

@CostasCF
Copy link

CostasCF commented Nov 5, 2023

Although I tested setting reducedMotion to false, it's not a viable solution because the problem still appears from time to time. I think it's a fundamental problem with how Reanimated and BottomSheet works as @Gyogle states.

@mattijsf
Copy link

mattijsf commented Nov 13, 2023

Inspired by the above answer, main difference being the import of ANIMATION_CONFIGS:

import { useBottomSheetSpringConfigs } from "@gorhom/bottom-sheet"
import { ANIMATION_CONFIGS } from "@gorhom/bottom-sheet/src/constants"
import { Platform } from "react-native"
import { ReduceMotion, WithTimingConfig, useReducedMotion } from "react-native-reanimated"

/**
 * https://github.com/gorhom/react-native-bottom-sheet/issues/1560
 *
 * Usage:
 * ```
 * const animationConfigs = useBottomSheetAnimationConfigsReducedMotionWorkaround()
 * <BottomSheetModal animationConfigs={animationConfigs} ...>
 * ```
 *
 * @returns
 */
export function useBottomSheetAnimationConfigsReducedMotionWorkaround():
  | WithTimingConfig
  | undefined {
  const reducedMotion = useReducedMotion()
  const iOSAnimationConfigWithoutReducedMotion = useBottomSheetSpringConfigs({
    ...ANIMATION_CONFIGS,
    reduceMotion: ReduceMotion.Never,
  })

  if (Platform.OS !== "ios" || !reducedMotion) return undefined

  return iOSAnimationConfigWithoutReducedMotion
}

@mattijsf
Copy link

@gorhom would the above be worth a v4 patch release?

@salman-ar-sar
Copy link

Tried the fix by @levibuzolic / @mattijsf. That works most of the time but got some weird issues when having multiple BottomSheets. Also tried disabling motion, that was very weird. Currently using the reanimated patch by @leymytel, that seems to be working fine.

@gorhom It would be really great if you can look into this.

@jlmosconi
Copy link

In my case, one of our users had the "Reduce Motion" option enabled and that caused the modals to not open. It was solved as follows

import {useReducedMotion} from 'react-native-reanimated'

...
const reducedMotion = useReducedMotion()

<BottomModal animateOnMount={!reducedMotion}

@hatem-72 Thanks!!

@marshallcool
Copy link

@leymytel this patch includes animation for users who have enabled motion reduction, I think not everyone will be happy with this behavior

@YaoHuiJi
Copy link

YaoHuiJi commented Dec 2, 2023

This issue will cause a lot of users to be completely unable to use the Apps that uses this library. If you have time, can you please have a look? @gorhom 💗

@christophby
Copy link

christophby commented Apr 22, 2024

The change from @hatem-72 should be included in the main code base of the bottom sheet.

Copy link

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@christophby
Copy link

Not stale

@nicolascavallin
Copy link

nicolascavallin commented May 24, 2024

With last release it was solved.

I've checked on my iOS app and it works as expected.

This is the commit


This issue should be closed @walterholohan @gorhom

@kimsergey
Copy link

This problem is still relevant. On version 4.6.3 it is much less common, but still exists

"react-native": "0.73.4",
"react-native-reanimated": "3.8.0",
"@gorhom/bottom-sheet": "4.6.3"

@mihaibulic2
Copy link

mihaibulic2 commented Jun 21, 2024

I fixed this in my app and wanted to share all I learned (some of this may be repeating what was said above):

BACKGROUND

  • this issue happens both on Android and iOS
  • it's due to reducedMotion being enabled (this can be enabled by the user OR auto-enabled by battery saver)
  • v4.6.3 somewhat solves the problem but also introduces a crash, so I'm not updating to that version
  • overriding the animation settings with ReduceMotion.Never was suggested above and is what v4.6.3 does but it's not a good fix because:
    1. it subverts the setting and is not what the user or battery saver want
    2. there are cases where it doesn't work; basically what I've noticed is that in some cases you now have the opposite problem: you can't dismiss the bottom sheet. This happens if you update a useState var that's used in the content of the bottom sheet while the bottom sheet is dismissing. I'm sure it happens in other cases but that's when I noticed it.
  • There is another workaround I used that honors the settings intention and seems to be more airtight:

MY WORKAROUND (THEORY)
What if we don't have the bottom sheet actually transitions when reducedMotion is on? This would side step the issue entirely. We have to worry about 2 cases: showing and dismissing. For showing, it's easy: just set animateOnMount={false}. This does seem to cause a problem for dismissing: when we set animateOnMount={false}, the bottomSheet ref's dismiss function can only dismiss the modal once for some reason. As a workaround for that, if we get a new ref on each dismissal, then each ref would only need to dismiss once. We can do that by changing the key value on each dismissal, forcing React to do a rerender. Code below!

MY WORKAROUND'S CODE
Here is a stripped down version of my code. Please lmk if it works for you or if you spot any problems!

const [bottomSheetBumpKey, setBottomSheetBumpKey] = useState<number>(0);
const reduceMotionEnabled = useReducedMotion();

return (<BottomSheetModalProvider>
    <BottomSheetModal
        index={0}
        ref={bottomSheetModalRef}
        snapPoints={bottomModalSnapPoints}
        backgroundComponent={null} // This is required to make the modal dark

        // HACKS! (needed as of v4.6.3)
        // 1. On android: when the user (or battery saver) enables "Reduced Motion" or "Reduced Animation", it makes the bottom sheet flicker / never appear.
        //    -- video: https://www.loom.com/share/d131efdd0b8e4e12b30afb7dd605a7e7
        //    -- details: https://github.com/gorhom/react-native-bottom-sheet/issues/1560#issuecomment-1774254176
        //    -- workaround: manually disable animations in this case (animateOnMount=false)
        // 2. On android: when animations are disabled (animateOnMount=false), the bottom sheet ref can only dismiss the UI once.
        //    -- workaround: change the key when we dismiss (if animateOnMount=false), forcing a rerender + a new ref
        // NOTE: there was an *attempt* to fix this in Bottom Sheet v4.6.3, but it's buggy and causes crashes - next version should fix?
        key={`bottom-sheet-modal-${bottomSheetBumpKey}`}
        animateOnMount={!reduceMotionEnabled}
        onDismiss={() => {
          if (reduceMotionEnabled) {
            setBottomSheetBumpKey((existing) => existing + 1);
          }
        }}>
          <MyCoolComponent/>
        )}
      </BottomSheetModal>
    </BottomSheetModalProvider>);

@yolpsoftware
Copy link

Can confirm, issue is no longer present in release 4.6.3, together with "react-native-reanimated": "3.6.2".

Copy link

github-actions bot commented Aug 3, 2024

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@nihilenz
Copy link

nihilenz commented Aug 3, 2024

not stale

@quocluongha
Copy link

quocluongha commented Aug 30, 2024

For me it is working fine with React Native old architecture. When I adapt new architecture and using 2 layers more of forwardRef, it does not show at all.

export const MainComponent = () => {
  const bottomSheetRef = useRef(null)

  return (
    <>
      <Button onPress={() => { bottomSheetRef.current?.present() }}> // <-- the present function is invoked but nothing happens
        Show sheet
      </Button>
      <ExampleSheet ref={bottomSheetRef} snapPoints={['50%']}>
    </>
  )
}

export const ExampleSheet = forwardRef((props, ref) => {
  return (
    <>
      ...
      <BaseSheet ref={ref} {...props}>
    </>
  )
})

export const BaseSheet = forwardRef((props, ref) => {
  return (
    <>
      ...
      <BaseSheet ref={ref} {...props}>
    </>
  )
})

@ramonjaspers
Copy link

ramonjaspers commented Sep 5, 2024

What is the current status on this issue at the moment? I am still using the following patch which feels outdated.

diff --git a/node_modules/react-native-reanimated/src/reanimated2/PlatformChecker.ts b/node_modules/react-native-reanimated/src/reanimated2/PlatformChecker.ts
index a2bcdee..9afa9e1 100644
--- a/node_modules/react-native-reanimated/src/reanimated2/PlatformChecker.ts
+++ b/node_modules/react-native-reanimated/src/reanimated2/PlatformChecker.ts
@@ -51,5 +51,5 @@ export function isReducedMotion() {
       ? // @ts-ignore Fallback if `window` is undefined.
         !window.matchMedia('(prefers-reduced-motion: no-preference)').matches
       : false
-    : !!(global as localGlobal)._REANIMATED_IS_REDUCED_MOTION;
+    : false;
 }

Copy link

github-actions bot commented Oct 6, 2024

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@nihilenz
Copy link

nihilenz commented Oct 6, 2024

not stale

Copy link

github-actions bot commented Nov 6, 2024

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

Copy link

This issue was closed because it has been stalled for 5 days with no activity.

@SimeonGrancharov
Copy link

Not stale

@DimaShumanskiy
Copy link

TypeError: 0, _reactNativeReanimated.useReducedMotion is not a function (it is undefined)
Снимок экрана 2024-11-27 в 12 01 58
"@gorhom/bottom-sheet": "^5.0.6",
"react-native": "0.72.6",
"react-native-reanimated": "3.3.0",

@levibuzolic
Copy link

@DimaShumanskiy useReducedMotion was added in Reanimated 3.4.0, you’ll need to update your packages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working no-issue-activity
Projects
None yet
Development

No branches or pull requests