Search code examples
reactjsreact-nativereact-navigationmobx-state-tree

Rerender AppNavigator on state change


I am trying to render certain nav stacks depending on a isAuthenticated state. The problem that I am having is that AppNavigator is only rendered on the first render and not with any other changes and I am not sure why. I have tried a useEffect in the AppNavigator component to set a secondary local state with the callback being isAuthenticated but no go. I put everything pertinent below. I appreciate any advice.

I have an AppNavigator that is being rendered in my app.tsx file.

  return (
    <ToggleStorybook>
      <ApolloProvider client={client}>
        <RootStoreProvider value={rootStore}>
          <SafeAreaProvider initialMetrics={initialWindowMetrics}>
            <ErrorBoundary catchErrors={"always"}>
              <AppNavigator
                initialState={initialNavigationState}
                onStateChange={onNavigationStateChange}
              />
            </ErrorBoundary>
          </SafeAreaProvider>
        </RootStoreProvider>
      </ApolloProvider>
    </ToggleStorybook>
  )

The AppNavigator is returning

export const AppNavigator = (props: NavigationProps) => {
  const { isAuthenticated } = useStores()
  const colorScheme = useColorScheme()
  useBackButtonHandler(canExit)

  return (
    <NavigationContainer
      ref={navigationRef}
      theme={colorScheme === "dark" ? DarkTheme : DefaultTheme}
      {...props}
    >
      <Stack.Navigator
        screenOptions={{
          headerShown: false,
        }}
      >
        {isAuthenticated ? (
          <Stack.Screen name="main" component={MainTabs} />
        ) : (
          <Stack.Screen name="signup" component={SignUpStack} />
        )}
      </Stack.Navigator>
    </NavigationContainer>
  )
}

I am using mob-state-x-tree for state management and have a setUser action that is called onAuthStateChanged per the firebase Auth docs. I'm using email and password login and sign up. I've logged the auth state changes they are working as expected.

  function onAuthStateChanged(user: any) {
    if (user) {
      if (rootStore) {
        rootStore.setUser(user)
        console.log("we have passed user to root store")
      }
    }

The setUser action sets a state isAuthenticated in a try catch

  setUser: flow(function* (firebaseUser) {
    try {
      const idToken = yield firebaseUser.getIdToken()
      yield AsyncStorage.setItem(
        '@lessns:token',
        idToken
      );
      self.isAuthenticated = true
      self.user = {id: firebaseUser.uid}
    } catch(err) {
      console.log(err, 'this is the first time ')
      self.isAuthenticated = false
    }
  }),

Solution

  • You need to make your AppNavigator component into an observer so that it will re-render when observable data it depends on changes.

    export const AppNavigator = observer((props: NavigationProps) => {
      // ...
    })