skip to Main Content

Im trying to implement this relatively popular bottom sheet in React Native by @gorhom.

My aim is to open the bottom sheet from another component. I’ve tried to follow this answer – here .

As far as i am aware, the forwarding of the ref seems correct. However, when i check the console log of the element being pressed – it is returned as ‘undefined’

Code below

BottomSheetModalComponent

export default function BottomSheetModalComponent({ref}) {

// ref
const bottomSheetModalRef = useRef<BottomSheetModal>(null);

// variables
const snapPoints = useMemo(() => ['25%', '50%'], []);

// callbacks
const handlePresentModalPress = useCallback(() => {
  ref.current?.present();
}, []);
const handleSheetChanges = useCallback((index: number) => {
  console.log('handleSheetChanges', index);
}, []);

// renders
return (
  <BottomSheetModalProvider>
    <View>
      <BottomSheetModal
        ref={ref}
        snapPoints={snapPoints}
        onChange={handleSheetChanges}
      >
        <View>
          <Text>Awesome 🎉</Text>
        </View>
      </BottomSheetModal>
    </View>
  </BottomSheetModalProvider>
);
};

Location Component

export default function LocationBar() {
   // Create Ref
  const userBottomSheetRef = useRef<BottomSheetModal>(null);

  // Pass ref into the bottom sheet component
  <LocationBottomSheet ref={userBottomSheetRef} snapPoints={["30%"]}/>

  return (
    <>
      <View style={{ flexDirection: "row" }}>
        <View style={styles.locationBar}>
          <Octicons style={styles.locationIcon} name="location" size={18} />
          <TouchableOpacity onPress={() => {

            // LINE BELOW RETURNS AS UNDEFINED
            userBottomSheetRef.current?.present()

          }}>
            <Text style={{fontSize: 17, fontWeight: '600'}}>{userLocation}</Text>
          </TouchableOpacity>
          <Ionicons style={styles.chevronIcon} name="chevron-down" size={12} />
        </View>
      </View>
    </>
  );
}

Thanks in advance

2

Answers


  1. Some things to note: I didn’t this test, so you may have to fix some things, but the way it goes is mentioned below. Secondly, bottom sheet needs to be rendered, so you need to place it in return.

    You need to use Forward Ref with useImperativeHandle, like this

    export default const BottomSheetModalComponent = forwardRef((props,ref)=> {
    
    
    // ref
    const bottomSheetModalRef = useRef<BottomSheetModal>(null);
    
    useImperativeHandle(ref,()=>({
    presentModal:()=>ref.current?.present() 
    })
    
    // variables
    const snapPoints = useMemo(() => ['25%', '50%'], []);
    
    // callbacks
    const handlePresentModalPress = useCallback(() => {
      ref.current?.present();
    }, []);
    const handleSheetChanges = useCallback((index: number) => {
      console.log('handleSheetChanges', index);
    }, []);
    
    // renders
    return (
      <BottomSheetModalProvider>
        <View>
          <BottomSheetModal
            ref={ref}
            snapPoints={snapPoints}
            onChange={handleSheetChanges}
          >
            <View>
              <Text>Awesome 🎉</Text>
            </View>
          </BottomSheetModal>
        </View>
      </BottomSheetModalProvider>
    );
    });
    

    Then use it in your component like this

    export default function LocationBar() {
       // Create Ref
      const userBottomSheetRef = useRef<BottomSheetModal>(null);
    
    
      return (
        <>
          <View style={{ flexDirection: "row" }}>
            <View style={styles.locationBar}>
              <Octicons style={styles.locationIcon} name="location" size={18} />
              <TouchableOpacity onPress={() => {
                userBottomSheetRef.current?.presentModal()
              }}>
                <Text style={{fontSize: 17, fontWeight: '600'}}>{userLocation}</Text>
              </TouchableOpacity>
              <Ionicons style={styles.chevronIcon} name="chevron-down" size={12} />
            </View>
          </View>
          <LocationBottomSheet ref={userBottomSheetRef} snapPoints={["30%"]}/>
        </>
      );
    }
    
    Login or Signup to reply.
  2. Don’t need another layer with useImperativeHandle. It works fine without it. Here is my example which I’ve tested.

    const ActionSheet = forwardRef<BottomSheetModal, BottomSheetModalProps>((props, ref) => {
        const renderBackDrop = useCallback((props: BottomSheetBackdropProps) => {
            return (
                <BottomSheetBackdrop {...props} opacity={0.5} animatedIndex={{ value: 1 }} pressBehavior={"close"} />
            )
        }, []);
    
        return (
            <BottomSheetModal
                ref={ref}
                {...props}
                containerStyle={{
                    borderTopLeftRadius: 24,
                    borderTopRightRadius: 24,
                }}
                backgroundStyle={{
                    backgroundColor: colors.slate[100]
                }}
                backdropComponent={renderBackDrop}
            >
                {props.children}
            </BottomSheetModal>
        );
    });
    

    When you call it, it is as follows:

    <ActionSheet 
                    ref={bottomSheetModalRef} 
                    snapPoints={snapPoints}
                    onDismiss={closeBottomSheet}
                >
                    <View><Text>Test</Text></View>
                </ActionSheet>
    

    For the BottomSheetBackdrop to close in Android (iOS works fine without it) just add the GestureHandlerRootView as per following example to your starting point file i.e. App.tsx / index.js

    const App = () => {    
      return (
        <>
          <Provider store={store}>
            <QueryClientProvider client={queryClient}>
              <SafeAreaProvider>
                <GestureHandlerRootView className='flex-1'>
                  <BottomSheetModalProvider>
                      {
                        IS_ANDROID && <StatusBar translucent backgroundColor={'transparent'} barStyle={colorScheme === 'dark' ? 'light-content' : 'dark-content'} />
                      }
                      <RootNavigation />
                  </BottomSheetModalProvider>
                </GestureHandlerRootView>
              </SafeAreaProvider>
            </QueryClientProvider>
          </Provider>    
          <Toast config={toastConfig} />
        </>
      );
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search