javascriptreact-nativeuse-refreact-ref

React native 'BottomSheetModalInternalContext' cannot be null


I'm using the BottomSheetModal to display a list of all countries in a bottom sheet. But I get the error message 'BottomSheetModalInternalContext' cannot be null! when navigating to the screen that includes the bottom modal.

I assume that the error occurs because I initialize the ref (bottomSheetModalRef) with null, but I don't know how else I can create the ref without passing in null.

Excerpt of the full error message

'BottomSheetModalInternalContext' cannot be null!

This error is located at:
    in ForwardRef(BottomSheetModal)
    in ForwardRef(BottomSheetModal) (at CountryPicker.tsx:89)
    in RCTView (at View.js:34)
    in View (created by ForwardRef)
    in ForwardRef (at CountryPicker.tsx:74)
    ...
//CountryPicker.tsx
import React, { useRef, useState, useEffect, useMemo } from 'react'
import {
  ActivityIndicator,
  TextInput,
  TouchableOpacity,
  StyleSheet,
} from 'react-native'
import axios from 'axios'
import { BottomSheetFlatList, BottomSheetModal } from '@gorhom/bottom-sheet'

import { Box, Text } from '../../theme/theme'

interface Props {
  onCountrySelected: (country: string) => void
  value: string
}

interface CountryProps {
  flag: string
  name: string
  alpha3Code: string
}
const Country = (country: CountryProps) => (
  <Box>
    <Text>{country.name}</Text>
  </Box>
)

const CountryPicker: React.FC<Props> = ({ onCountrySelected, value }) => {
  const [countries, setCountries] = useState<CountryProps[]>([])
  const [isLoading, setIsLoading] = useState(false)
  const bottomSheetModalRef = useRef<BottomSheetModal>(null)

  const handlePresentPress = () => {
    if (bottomSheetModalRef.current) bottomSheetModalRef.current.present()
  }
  const snapPoints = useMemo(() => ['20%', '40%'], [])

  const getAllCountries = async () => {
    setIsLoading(true)
    const res = await axios.get(
      'https://restcountries.eu/rest/v2/all?fields=name;flag;alpha3Code',
    )
    const data = await res.data
    setCountries(data)
    setIsLoading(false)
  }

  const getCountryByCode = async (code: string) => {
    setIsLoading(true)
    const res = await axios.get(
      `https://restcountries.eu/rest/v2/alpha/${code}?fields=name;flag;alpha3Code`,
    )
    const data = await res.data
    setCountries(data)
    setIsLoading(false)
  }

  const renderCountry = (country: CountryProps) => {
    return (
      <Country
        flag={country.flag}
        name={country.name}
        alpha3Code={country.alpha3Code}
      />
    )
  }

  useEffect(() => {
    getAllCountries()
  }, [])

  return (
    <Box>
      <TouchableOpacity onPress={handlePresentPress}>
        <Box marginVertical="xs">
          <Box
            paddingVertical="inputS"
            paddingHorizontal="inputM"
            borderRadius="s"
            backgroundColor="inputBG"
            borderColor="inputBG"
            borderWidth={2}
          >
            <TextInput placeholder="Country" value={value} editable={false} />
          </Box>
        </Box>
      </TouchableOpacity>
      <BottomSheetModal ref={bottomSheetModalRef} snapPoints={snapPoints}>
        {isLoading ? (
          <ActivityIndicator />
        ) : (
          <BottomSheetFlatList
            data={countries}
            keyExtractor={(item: CountryProps) => item.alpha3Code}
            renderItem={(countries) => renderCountry(countries.item)}
            contentContainerStyle={styles.contentContainer}
            onRefresh={getAllCountries}
            bounces={false}
          />
        )}
      </BottomSheetModal>
    </Box>
  )
}

const styles = StyleSheet.create({
  contentContainer: {
    flex: 1,
  },
})

export default CountryPicker

Solution

  • confirm you surround your App.tsx file with BottomSheetModalProvider

      return (
        <BottomSheetModalProvider>
          ....
        </BottomSheetModalProvider>
      );