Syed Rajin
Syed Rajin

Reputation: 71

How to use stack navigator declared below another stack in react native

I am trying to put a stack navigator inside another stack navigator like this

const ProfileStack = createStackNavigator(
 {
   Profile: { screen: ProfileScreen },
   Comment: { screen: CommentStack },
 }
);

const CommentStack = createStackNavigator(
 {
   Comment: { screen: CommentScreen },
   Profile: { screen: ProfileStack },
 }
);

since CommentStack is declared after ProfileStack I am not able to achieve what I want. Is there any way I can do the same thing like this in react-navigation 4?

EDIT: I am trying to make a social media app and the above way doesn't work. The current way I am doing it in this way:

const ProfileStack = createStackNavigator(
 {
   Profile: { screen: ProfileScreen },
   Comment: { screen: CommentScreen },
 }
);

const CommentStack = createStackNavigator(
 {
   Comment: { screen: CommentScreen },
   Profile: { screen: ProfileStack },
 }
);

But when someone goes to someone's profile and goes to comment on his/her post then visits a profile of a commenter react-native navigates back to the profile screen. What I want is to send to a new profile stack so it can work in a loop. Sorry English is not my first language

Upvotes: 0

Views: 696

Answers (1)

bas
bas

Reputation: 15462

Like I mentioned in my comment you can't have two navigators that have each other as screens. You can have one navigator that has another navigator as a screen. But from what I understand from your question I don't think there is even a need for a ProfileStack and a CommentStack.

I propose instead to have one navigator that holds the CommentScreen and the ProfileScreen and to let your data dictate what to show and where to go. Here is some code to show to illustrate what I mean:

/* The users in your system identified by an id 
   and the comments they have on their profile */
const profileData = [
  {
    userId: 1,
    comments: [
      { message: 'message from 2', commenter: 2 },
      { message: 'message from 3', commenter: 3 },
    ],
  },
  {
    userId: 2,
    comments: [{ message: 'message from 1', commenter: 1 }],
  },
  {
    userId: 3,
    comments: [{ message: 'message from 2', commenter: 2 }],
  },
];

/* A simple profile screens that displays a user's profile
   and that has a button to navigate to the comment page. */
const ProfileScreen = ({ navigation }) => {
  const userId = navigation.getParam('userId');
  return (
    <View>
      <Text>{`Profile of user ${userId}`}</Text>
      <Button
        title="go to comments"
        onPress={() => {
          navigation.push('Comment', { userId });
        }}
      />
    </View>
  );
};

/* The comment screen shows all the comments a user has 
   and displays a button next to a comment to go to that 
   commenter's profile */
const CommentScreen = ({ navigation }) => {
  const userId = navigation.getParam('userId');
  return (
    <View>
      <Text>{`Comments of user ${userId}`}</Text>
      {profileData
        .filter((user) => user.userId == userId)[0]
        .comments.map((comment) => {
          return (
            <View style={{ flexDirection: 'row' }}>
              <Text>{comment.message}</Text>
              <Button
                title={`Go to ${comment.commenter}'s Profile'`}
                onPress={() =>
                  navigation.push('Profile', { userId: comment.commenter })
                }
              />
            </View>
          );
        })}
    </View>
  );
};

// The screen you use to navigate to the profile screen
const InitialScreen = ({ navigation }) => {
  return (
    <View>
      <Text>{`InitialScreen`}</Text>
      <Button
        title="Go to profile 1"
        onPress={() =>
          navigation.navigate('Profile', {
            userId: 1,
          })
        }
      />
    </View>
  );
};

const ExampleNavigator = createStackNavigator({
  Profile: { screen: ProfileScreen },
  Comment: { screen: CommentScreen },
});

const MainNavigator = createStackNavigator(
  {
    SomeInitialScreen: { screen: InitialScreen },
    ExampleNavigator: { screen: ExampleNavigator },
  },
  {
    headerMode: 'none',
  }
);

export default createAppContainer(MainNavigator);

So the idea here is that you have an array that holds the data of your users where each user is identifiable by an id, userId in this example. A user could also have a comments property that holds the message from a commenter and the commenter's id, commenter.

As far as navigation goes the only thing we really have to do if we have access to this data an implement this kind of structure is to pass a userId when navigating to the profile page and when navigating to the comment page.


I have added a MainNavigator in this example, because from the question I presumed you start from another screen, like a home screen, before you navigate to a specific user's profile.

Update

OP has indicated that the duplicating screen bug was solved by disabling the navigation buttons after pressing them. This is definitely an easy and good way to go about it, but since it might be helpful to someone I'll also explain how you could go about preventing duplication in the navigation state.

Duplication in this case meaning that we've already seen a particular route and userId combination. I've created a function that replaces the calls to push in the previous example:

function navigateByPushWithoutDuplicates(routeName, userId, navigation) {
  const screenNavigatedToBefore = navigation
    .dangerouslyGetParent()
    .state.routes.find(
      (element) =>
        element.params.userId === userId && element.routeName === routeName
    );
  if (screenNavigatedToBefore !== undefined) {
    const navigateAction = NavigationActions.navigate({
      routeName: routeName,
      params: { userId: userId },
      key: screenNavigatedToBefore.key,
    });
    navigation.dispatch(navigateAction);
  } else {
    navigation.push(routeName, { userId: userId });
  }
}

With this function you could do something like this:

navigateByPushWithoutDuplicates("Comment", userId, navigation);

and this:

navigateByPushWithoutDuplicates("Profile", comment.commenter, navigation);

Upvotes: 1

Related Questions