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

[v5] Modal reopens while opening second, and while dismissing first modal #1561

Closed
devoren opened this issue Oct 5, 2023 · 33 comments
Closed
Labels
bug Something isn't working no-issue-activity

Comments

@devoren
Copy link

devoren commented Oct 5, 2023

Bug

Maybe similiar to #204
I have 2 modals and global state. When global state updates i dismiss first modal and open second modal

  1. If i dismiss first modal, then: second modal reopens while opening
1.mp4
  1. If i close first modal, then: second modal opens fine but if i close second modal, first modal reopens. (or is even not closed?)
2.mp4

Environment info

Library Version
@gorhom/bottom-sheet ^5.0.0-alpha.3
react-native 0.72.5
react-native-reanimated ^3.5.1
react-native-gesture-handler ~2.12.0

Steps To Reproduce

  1. Create 2 modals in 2 component
  2. Open first modal
  3. Close first modal and present second modal

Describe what you expected to happen:

  1. When I dismiss first modal and present second modal
  2. Second modal wont reopen

Reproducible sample code

I thought that the modal is reopened because the global state changes, but this is not the case: while opening the second modal and closing the first modal, the second modal still reopens

const presentBottomSheet = useCallback(() => {
bottomSheetModalRef.current?.present();
}, []);

const closeBottomSheet = useCallback(() => {
bottomSheetModalRef.current?.dismiss();
}, []);

const presentTeamBottomSheet = useCallback(() => {
bottomSheetModalRef.current?.dismiss();
teamBottomSheetModalRef.current?.present();
}, []);

const closeTeamBottomSheet = useCallback(() => {
teamBottomSheetModalRef.current?.dismiss();
}, []);

return <>
...
<BottomSheetModal
		ref={bottomSheetModalRef}
		index={0}
		snapPoints={snapPoints}
		backdropComponent={renderBackdrop}
		handleComponent={null}
		enableOverDrag={false}
		enablePanDownToClose={false}>
		<BottomSheetView
			style={{
				flex: 1,
			}}>
			<View style={styles.header}>
				<Text style={styles.name}>Список участников</Text>
				<Pressable style={styles.close} onPress={closeBottomSheet}>
					<View style={styles.icon}>
						<Icons.Close
							color={colors.gray400}
							width={scale.lg}
							height={scale.lg}
						/>
					</View>
				</Pressable>
			</View>
			<ScrollView
				contentContainerStyle={{
					paddingBottom: insets.bottom + scale.md,
					marginHorizontal: scale.md,
				}}>
				{teams.map((team) => {
					return (
						<Pressable
							key={team.id}
							style={styles.item}
							onPress={() => {
								presentTeamBottomSheet();
							}}>
							<TeamItem team={team} />
						</Pressable>
					);
				})}
			</ScrollView>
		</BottomSheetView>
	</BottomSheetModal>
	<BottomSheetModal
		ref={teamBottomSheetModalRef}
		snapPoints={animatedSnapPoints as any}
		contentHeight={animatedContentHeight}
		handleHeight={animatedHandleHeight}
		backdropComponent={renderTeamBackdrop}
		bottomInset={scale.md + insets.bottom}
		handleComponent={null}
		enableOverDrag={false}
		enablePanDownToClose={false}>
		<BottomSheetView
			style={{
				flex: 1,
			}}
			onLayout={handleContentLayout}>
			<View style={styles.header}>
				<Text style={styles.name}>{t('participateToOrder')}</Text>
				<Pressable style={styles.close} onPress={closeTeamBottomSheet}>
					<View style={styles.icon}>
						<Icons.Close
							color={colors.gray400}
							width={scale.lg}
							height={scale.lg}
						/>
					</View>
				</Pressable>
			</View>
			<View style={styles.contentContainer}>
				<Text>Some longh text about team round</Text>
				<Button label={t('joinToOrder')} onPress={joinToOrder} />
			</View>
		</BottomSheetView>
	</BottomSheetModal>
</>
@devoren devoren added the bug Something isn't working label Oct 5, 2023
@devoren devoren changed the title [v5] Modal reopens while opening, while dismissing another modal [v5] Modal reopens while opening second, and while dismissing first modal Oct 5, 2023
@enchorb
Copy link

enchorb commented Oct 5, 2023

Same issue here, this was fine in v4

@devoren
Copy link
Author

devoren commented Oct 15, 2023

@enchorb yeah same :( iam using v5 because of web support

@Simoon-F
Copy link

Simoon-F commented Oct 23, 2023

Same problem in v4.5.1.

  1. Close the first modal and open the second modal
  2. Close the second modal, the first modal pops up automatically

My temporary solution is to add a delay function in the middle :

xxx.current?.close();

await delay(600);

xxx.current?.present();

@devoren
Copy link
Author

devoren commented Oct 23, 2023

@Simoon-Fyes, I use delay too, but it looks laggy :(

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.

@devoren
Copy link
Author

devoren commented Nov 22, 2023

the bug still exists

@Bad-Listener
Copy link

yeap, I confirm, still exists!

@michaelbrant
Copy link

anyone find a work around?

@devoren
Copy link
Author

devoren commented Dec 20, 2023

I'm using delays now, but it's still buggy

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.

@devoren
Copy link
Author

devoren commented Jan 19, 2024

the bug still exists

@cristiano-linvix
Copy link

any suggestion to solve this problem?

@michaelbrant
Copy link

my workaround is..

  1. Create a custom component called Sheet to use for all bottom sheets in my app. (see code below)
  2. manage the close state from a parent component. So if the modal closes, the parent component sets a state variable sheetOpen to false.
  3. If sheetOpen is false, don't render the modal. This ensures there aren't other instances of the Sheet opening.
    if (!sheetOpen) { return null; }

The downside to this approach is that you don't get the animation of the modal closing when it switches to sheetOpen=false. I would love to use a better solution if someone has one!! This is a work around.

const Sheet = ({
  sheetOpen,
  setSheetOpen,
  bottomSheetProps,
  children,
  onSheetClose,
  footerComponent,
}: {
  sheetOpen: boolean;
  setSheetOpen: React.Dispatch<React.SetStateAction<boolean>>;
  bottomSheetProps?: BottomSheetProps;
  children?: React.ReactNode;
  onSheetClose?: () => void;
  footerComponent?: React.FC<BottomSheetFooterProps>;
}) => {
  const bottomSheetRef = useRef<BottomSheet>(null);

  // Snap points
  const snapPoints = useMemo(() => ["80%"], []);

  // Handle sheet state changes
  const handleSheetChanges = useCallback(
    (index: number) => {
      if (index < 0) {
        setSheetOpen(false);
        Keyboard.dismiss();
        onSheetClose && onSheetClose();
      } else {
        setSheetOpen(true);
      }
    },
    [setSheetOpen, onSheetClose]
  );

  // Sync the sheet open state with the component's prop
  useEffect(() => {
    if (sheetOpen) {
      bottomSheetRef.current?.expand();
    } else {
      bottomSheetRef.current?.close();
    }
  }, [sheetOpen]);

  // Bottom sheet has a bug where another modal will open after closing one, so this is a workaround
  // to not render the modal at all if it's not open.
  // In order for this to work, the initial index had to be set to 0, which is the open state. -1 is closed.
  if (!sheetOpen) {
    return null;
  }
  return (
    <Portal>
      <BottomSheet
        footerComponent={footerComponent}
        ref={bottomSheetRef}
        index={0}
        snapPoints={snapPoints}
        onChange={handleSheetChanges}
        enablePanDownToClose={true}
        backgroundStyle={{
          borderTopRightRadius: 30,
          borderTopLeftRadius: 30,
          backgroundColor: "#f8f6ff",
        }}
        {...bottomSheetProps}
      >
        <View style={styles.contentContainer}>{children}</View>
      </BottomSheet>
    </Portal>
  );
};

@flodlc
Copy link

flodlc commented Feb 15, 2024

Same here

@cristiano-linvix
Copy link

my workaround is..

  1. Create a custom component called Sheet to use for all bottom sheets in my app. (see code below)
  2. manage the close state from a parent component. So if the modal closes, the parent component sets a state variable sheetOpen to false.
  3. If sheetOpen is false, don't render the modal. This ensures there aren't other instances of the Sheet opening.
    if (!sheetOpen) { return null; }

The downside to this approach is that you don't get the animation of the modal closing when it switches to sheetOpen=false. I would love to use a better solution if someone has one!! This is a work around.

const Sheet = ({
  sheetOpen,
  setSheetOpen,
  bottomSheetProps,
  children,
  onSheetClose,
  footerComponent,
}: {
  sheetOpen: boolean;
  setSheetOpen: React.Dispatch<React.SetStateAction<boolean>>;
  bottomSheetProps?: BottomSheetProps;
  children?: React.ReactNode;
  onSheetClose?: () => void;
  footerComponent?: React.FC<BottomSheetFooterProps>;
}) => {
  const bottomSheetRef = useRef<BottomSheet>(null);

  // Snap points
  const snapPoints = useMemo(() => ["80%"], []);

  // Handle sheet state changes
  const handleSheetChanges = useCallback(
    (index: number) => {
      if (index < 0) {
        setSheetOpen(false);
        Keyboard.dismiss();
        onSheetClose && onSheetClose();
      } else {
        setSheetOpen(true);
      }
    },
    [setSheetOpen, onSheetClose]
  );

  // Sync the sheet open state with the component's prop
  useEffect(() => {
    if (sheetOpen) {
      bottomSheetRef.current?.expand();
    } else {
      bottomSheetRef.current?.close();
    }
  }, [sheetOpen]);

  // Bottom sheet has a bug where another modal will open after closing one, so this is a workaround
  // to not render the modal at all if it's not open.
  // In order for this to work, the initial index had to be set to 0, which is the open state. -1 is closed.
  if (!sheetOpen) {
    return null;
  }
  return (
    <Portal>
      <BottomSheet
        footerComponent={footerComponent}
        ref={bottomSheetRef}
        index={0}
        snapPoints={snapPoints}
        onChange={handleSheetChanges}
        enablePanDownToClose={true}
        backgroundStyle={{
          borderTopRightRadius: 30,
          borderTopLeftRadius: 30,
          backgroundColor: "#f8f6ff",
        }}
        {...bottomSheetProps}
      >
        <View style={styles.contentContainer}>{children}</View>
      </BottomSheet>
    </Portal>
  );
};

This works for me!!!

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.

@devoren
Copy link
Author

devoren commented Mar 18, 2024

bug still exists

@POLEC4T
Copy link

POLEC4T commented Apr 3, 2024

my workaround is..

  1. Create a custom component called Sheet to use for all bottom sheets in my app. (see code below)
  2. manage the close state from a parent component. So if the modal closes, the parent component sets a state variable sheetOpen to false.
  3. If sheetOpen is false, don't render the modal. This ensures there aren't other instances of the Sheet opening.
    if (!sheetOpen) { return null; }

The downside to this approach is that you don't get the animation of the modal closing when it switches to sheetOpen=false. I would love to use a better solution if someone has one!! This is a work around.

const Sheet = ({
  sheetOpen,
  setSheetOpen,
  bottomSheetProps,
  children,
  onSheetClose,
  footerComponent,
}: {
  sheetOpen: boolean;
  setSheetOpen: React.Dispatch<React.SetStateAction<boolean>>;
  bottomSheetProps?: BottomSheetProps;
  children?: React.ReactNode;
  onSheetClose?: () => void;
  footerComponent?: React.FC<BottomSheetFooterProps>;
}) => {
  const bottomSheetRef = useRef<BottomSheet>(null);

  // Snap points
  const snapPoints = useMemo(() => ["80%"], []);

  // Handle sheet state changes
  const handleSheetChanges = useCallback(
    (index: number) => {
      if (index < 0) {
        setSheetOpen(false);
        Keyboard.dismiss();
        onSheetClose && onSheetClose();
      } else {
        setSheetOpen(true);
      }
    },
    [setSheetOpen, onSheetClose]
  );

  // Sync the sheet open state with the component's prop
  useEffect(() => {
    if (sheetOpen) {
      bottomSheetRef.current?.expand();
    } else {
      bottomSheetRef.current?.close();
    }
  }, [sheetOpen]);

  // Bottom sheet has a bug where another modal will open after closing one, so this is a workaround
  // to not render the modal at all if it's not open.
  // In order for this to work, the initial index had to be set to 0, which is the open state. -1 is closed.
  if (!sheetOpen) {
    return null;
  }
  return (
    <Portal>
      <BottomSheet
        footerComponent={footerComponent}
        ref={bottomSheetRef}
        index={0}
        snapPoints={snapPoints}
        onChange={handleSheetChanges}
        enablePanDownToClose={true}
        backgroundStyle={{
          borderTopRightRadius: 30,
          borderTopLeftRadius: 30,
          backgroundColor: "#f8f6ff",
        }}
        {...bottomSheetProps}
      >
        <View style={styles.contentContainer}>{children}</View>
      </BottomSheet>
    </Portal>
  );
};

Hey, I'm trying to reproduce your workaround. However you didn't provide your imports, could you tell me what is "Portal" ? Also, are you importing "BottomSheet" from "@gorhom/bottom-sheet" ? Because it says this :

"'"@gorhom/bottom-sheet"' has no exported member named 'BottomSheet'. Did you mean 'useBottomSheet'?ts(2724)
import BottomSheet"

when I type

import { BottomSheet } from "@gorhom/bottom-sheet";

Thank you.

@michaelbrant
Copy link

Here are my imports, I'm not sure why BottomSheet isn't resolving for you..

import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
import BottomSheet, { BottomSheetBackdrop, BottomSheetFooterProps, BottomSheetProps } from "@gorhom/bottom-sheet";
import { BottomSheetDefaultBackdropProps } from "@gorhom/bottom-sheet/lib/typescript/components/bottomSheetBackdrop/types";
import { Keyboard, StyleSheet, View } from "react-native";
import { Portal } from "@gorhom/portal";

@POLEC4T
Copy link

POLEC4T commented Apr 3, 2024

Thank you very much, It was because I was writing "{ BottomSheet }" instead of "BottomSheet"

@POLEC4T
Copy link

POLEC4T commented Apr 3, 2024

H

Could you please provide an example of a use the Sheet component you created ? Thank you.

@michaelbrant
Copy link

    <Sheet
      sheetOpen={sheetOpen}
      setSheetOpen={setSheetOpen}
      bottomSheetProps={{
        snapPoints: ["70%"],
        backgroundStyle: {
          backgroundColor: "#5423E7",
        },
        children: <></>,
      }}
      key={"recommend-sheet"}
    >
     put stuff in the sheet here
    </Sheet>```
    
    setSheetOpen is a function to set state and sheetOpen is the boolean from useState

Copy link

github-actions bot commented May 4, 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.

@POLEC4T
Copy link

POLEC4T commented May 4, 2024

bug still exists

@SorooshDeveloper
Copy link

bug still exists

@MrgSub
Copy link

MrgSub commented Jun 5, 2024

I solved this issue using a sleepAsync function.
The bottom sheet needs to be fully closed before any rendering changes happen on your screen, otherwise it pops back up (for some reason).
Here's what I did;

	
bottomSheetModalRef.current?.close();
await sleepAsync(200);
// Do anything else with the page

@SorooshDeveloper
Copy link

@gorhom i get this issue when i set the first modal to be enableDismissonClose={false}

@igorm-cr
Copy link

Facing this and similar possibly related issue in latest alpha as well.

Using dismiss() makes the dismissed sheet pop back up no matter what you do in case there is some change in state or data that triggers rendering in the sheet.

Using close() fixes the issue partially but in my case I also had to delay any data/state changes for more than 250ms (this is the time for the sheet to fully animate).

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.

Copy link

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

@R0LLeX
Copy link

R0LLeX commented Nov 1, 2024

bug still exists

@BR1G00
Copy link

BR1G00 commented Nov 11, 2024

You can fix this using this props on the modalstackBehavior="replace"

@kipertech
Copy link

kipertech commented Dec 2, 2024

Maybe not perfect but this is what I'm doing to work around both the "Reduce Motion" issue and this issue. When "Reduce Motion" is off then I just don't use this animation override at all.

(I am on 4.4.7 of this library, which doesn't have the ReduceMotion.Never implemented always).

// Reduce Motion issue: https://github.com/gorhom/react-native-bottom-sheet/pull/1743
const animationConfigs = useBottomSheetSpringConfigs({
    reduceMotion: ReduceMotion.Never,
    // Make it super fast so that it doesn't get blocked by subsequent animations / setStates
    mass: 0.1,
    duration: 10,
});

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