typescriptreact-nativereact-navigation

Wrap nested navigator in Context with Static API


I'm using the new Static API of React Navigation version 7, and I'm struggling to wrap a nested navigator in a context.

My app shows the HomeScreen with a button that can modally present the ProfileScreen. This is wrapped in it's own native stack navigator called ProfileStack. All screens within the ProfileStack should be able to access ProfileProvider but the HomeScreen shouldn't.

Here's the code:

// Screens

function HomeScreen() {
  const navigation = useNavigation();

  return (
    <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
      <Text>Home Screen</Text>
      <Button
        title="Show Profile modal"
        onPress={() => navigation.navigate('ProfileStack')}
      />
    </View>
  );
}

function ProfileScreen() {
  const navigation = useNavigation();

  return (
    <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
      <Text>Profile</Text>
      <Button
        title="Go to More Profile"
        onPress={() =>
          navigation.navigate('ProfileStack', {screen: 'MoreProfile'})
        }
      />
    </View>
  );
}

function MoreProfileScreen() {
  return (
    <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
      <Text>More Profile</Text>
    </View>
  );
}

// Context

type ProfileContextType = {
  name?: string;
  setName: (name: string) => void;
};

const ProfileContext = React.createContext<ProfileContextType | undefined>(
  undefined,
);

const ProfileProvider = ({children}: {children: React.ReactNode}) => {
  const [name, setName] = React.useState<string>();

  return (
    <ProfileContext.Provider value={{name, setName}}>
      {children}
    </ProfileContext.Provider>
  );
};

// Navigators

const ProfileStackNavigator = createNativeStackNavigator({
  screens: {
    Profile: ProfileScreen,
    MoreProfile: MoreProfileScreen,
  },
});

const RootStackNavigator = createNativeStackNavigator({
  screens: {
    Home: HomeScreen,
    ProfileStack: {
      screen: ProfileStackNavigator,
      options: {
        presentation: 'modal',
        headerShown: false,
      },
    },
  },
});

const Navigation = createStaticNavigation(RootStackNavigator);

// App

function App(): React.JSX.Element {
  return <Navigation />;
}

export default App;

How can I wrap ProfileStackNavigator in ProfileProvider so that both ProfileScreen and MoreProfileScreen can access the shared useState of ProfileContext?


Solution

  • You can use the layout param in createNativeStackNavigator to wrap your navigator with other components.

    const RootStackNavigator = createNativeStackNavigator({
      screens: {
        Home: HomeScreen,
        ProfileStack: {
          screen: ProfileStackNavigator,
          options: {
            presentation: 'modal',
            headerShown: false,
          },
        },
      },
      layout: props => <ProfileProvider>{props.children}</ProfileProvider>
    });
    

    You can test it here: https://snack.expo.dev/Y8yZqOPGf1F1RmDemC5by