kyriazo
kyriazo

Reputation: 43

React Navigation nesting creates infinite loop

So I am using react navigation v5, and have run to this problem. My navigation starts at:

    
      const RootAppNavigator = () => {
        return(
        <RootApp.Navigator>
        <RootApp.Screen name="App" component={NavigationScreen} />
      </RootApp.Navigator>
        )
      }
    
    export default function App() {
    
      let [fontsLoaded] = useFonts({
        Lobster_400Regular,
      });
    
        // if (!fontsLoaded) {
        //   return <AppLoading />;
        // }
        return <NavigationContainer><RootAppNavigator /></NavigationContainer>;
      }

User is then redirected to NavigationScreen:

const HomeStack = createStackNavigator();
const ProfileStack = createStackNavigator();
const Tab = createBottomTabNavigator();



export default function NavigationScreen() {

  const HomeStackNavigator = () => {
    return (
    <HomeStack.Navigator initialRouteName="Home">
      <HomeStack.Screen name="Home" component={HomeScreen}/>
    </HomeStack.Navigator>
    )
  }

  const ProfileStackNavigator = () => {
    return(
    <ProfileStack.Navigator initialRouteName="Profile" screenOptions={{headerStyle: {elevation: 0},cardStyle: {backgroundColor: '#ffffff'}}}>
      <ProfileStack.Screen name="Profile" component={ProfileScreen}/>
    </ProfileStack.Navigator>
    )
  }
  
  return (
      <Tab.Navigator
      initialRouteName="HomeS"
      >
        <Tab.Screen name="HomeS" component={HomeStackNavigator} />
        <Tab.Screen name="ProfileS" component={ProfileStackNavigator} />
      </Tab.Navigator>
  );
}

Now for some reason I cannot understand, Home screen is rendered at first, but as soon as I click the profile screen on my Tab Navigator, the Profile screen is rendered but an infinite loop starts between those two screens.(Found it by logging in their componentDidMount function, switching is not visible on mobile though.). At some point the looping stops and I get a Maximum update depth exceeded error.

Any help would be much appreciated.

Upvotes: 1

Views: 2331

Answers (2)

evan g
evan g

Reputation: 94

Move your navigators out of render method. (I believe this general fix applies to React Navigation 5 & 6.)

You can nest navigators like you've done, by putting nested navigators within the render component of a screen: NavigationContainer > Navigator > Screen > Component > NestedNavigator.

What you cannot do is define the nested navigators themselves within the render method of the screen.

The nested navigators are re-rendered whenever the screen is rendered. I'm not clear on the implementation details, but error messages I've seen imply that the gist of why this happens is that navigators use hooks to update the parent navigators' state, which causes the screen to re-render (then rendering the navigator again, and so on ad infinitum).

Something like this should solve the problem (notice how HomeStackNavigator and ProfileStackNavigator are moved outside the render method of NavigationScreen):

const ProfileStack = createStackNavigator();
const Tab = createBottomTabNavigator();


const HomeStackNavigator = () => {
  return (
  <HomeStack.Navigator initialRouteName="Home">
    <HomeStack.Screen name="Home" component={HomeScreen}/>
  </HomeStack.Navigator>
  )
}

const ProfileStackNavigator = () => {
  return(
  <ProfileStack.Navigator initialRouteName="Profile" screenOptions={{headerStyle: {elevation: 0},cardStyle: {backgroundColor: '#ffffff'}}}>
    <ProfileStack.Screen name="Profile" component={ProfileScreen}/>
  </ProfileStack.Navigator>
  )
}

export default function NavigationScreen() {
  
  return (
      <Tab.Navigator
      initialRouteName="HomeS"
      >
        <Tab.Screen name="HomeS" component={HomeStackNavigator} />
        <Tab.Screen name="ProfileS" component={ProfileStackNavigator} />
      </Tab.Navigator>
  );
}```

Upvotes: 0

kyriazo
kyriazo

Reputation: 43

So after checking back with a clear mind, I found what caused it. I used an extra unnecessary navigator in App.js


      const RootAppNavigator = () => {
        return(
        <RootApp.Navigator>
        <RootApp.Screen name="App" component={NavigationScreen} />
      </RootApp.Navigator>
        )
      }

So when I changed this

return <NavigationContainer><RootAppNavigator /></NavigationContainer>;

to this

return <NavigationContainer><NavigationScreen /></NavigationContainer>;

it worked.

Problem is I cannot understand why, so I would be grateful if someone could explain.

Upvotes: 1

Related Questions