Как использовать навигатор стека, объявленный под другим стеком в react native

#reactjs #react-native #react-navigation

#reactjs #react-native #react-навигация

Вопрос:

Я пытаюсь поместить навигатор стека в другой навигатор стека следующим образом

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

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

поскольку CommentStack объявлен после ProfileStack, я не могу достичь того, чего хочу.
Могу ли я каким-либо образом сделать то же самое, что и в react-navigation 4?

РЕДАКТИРОВАТЬ: я пытаюсь создать приложение для социальных сетей, и описанный выше способ не работает. В настоящее время я делаю это таким образом:

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

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

Но когда кто-то заходит в чей-то профиль и комментирует его / ее сообщение, а затем посещает профиль комментатора, react-native переходит обратно на экран профиля. Я хочу отправить в новый стек профилей, чтобы он мог работать в цикле. Извините, английский не мой родной язык

Комментарии:

1. Почему вы это пытаетесь? Я думаю, вы вообще не понимаете, как работает react navigation

2. Я действительно не понимаю, что вы пытаетесь здесь сделать. Эти навигаторы не могут одновременно иметь друг друга в качестве экранов. Не могли бы вы подробнее рассказать о варианте использования? Возможно, это делает его более понятным.

Ответ №1:

Как я упоминал в своем комментарии, у вас не может быть двух навигаторов, которые имеют друг друга в качестве экранов. У вас может быть один навигатор, у которого есть другой навигатор в качестве экрана. Но из того, что я понимаю из вашего вопроса, я не думаю, что есть даже необходимость в ProfileStack и CommentStack .

Я предлагаю вместо этого иметь один навигатор, который содержит CommentScreen и ProfileScreen , и позволить вашим данным определять, что показывать и куда идти. Вот некоторый код, который нужно показать, чтобы проиллюстрировать, что я имею в виду:

 /* 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);
  

Итак, идея здесь в том, что у вас есть массив, который содержит данные ваших пользователей, где каждый пользователь идентифицируется по идентификатору, userId в этом примере. У пользователя также может быть comments свойство, которое содержит message от комментатора и идентификатор комментатора, commenter .

Что касается навигации, единственное, что нам действительно нужно сделать, если у нас есть доступ к этим данным, чтобы реализовать такую структуру, — это передать userId при переходе на страницу профиля и при переходе на страницу комментариев.


Я добавил MainNavigator в этом примере, потому что из вопроса я предположил, что вы начинаете с другого экрана, например, с домашнего экрана, прежде чем перейти к профилю конкретного пользователя.

Обновить

OP указал, что ошибка с дублированием экрана была устранена путем отключения кнопок навигации после их нажатия. Это определенно простой и хороший способ сделать это, но поскольку это может быть кому-то полезно, я также объясню, как вы могли бы предотвратить дублирование в состоянии навигации.

Дублирование в данном случае означает, что мы уже видели определенный маршрут и userId комбинацию. Я создал функцию, которая заменяет вызовы push в предыдущем примере:

 function navigateByPushWithoutDuplicates(routeName, userId, navigation) {
  const screenNavigatedToBefore = navigation
    .dangerouslyGetParent()
    .state.routes.find(
      (element) =>
        element.params.userId === userId amp;amp; 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 });
  }
}
  

С помощью этой функции вы могли бы сделать что-то вроде этого:

 navigateByPushWithoutDuplicates("Comment", userId, navigation);
  

и это:

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

Комментарии:

1. Спасибо за ваш ответ, но navigation.navigate(‘Profile’, { userId: comment.commenter }) возвращает меня к экрану профиля вместо того, чтобы отправлять меня на новый экран профиля

2. Что вы имеете в виду, когда говорите «новый» экран профиля? Он переходит к тому же компоненту, но содержимое изменилось, это важная часть, верно?

3. Я думаю, я понимаю, что вы имеете в виду, вы можете использовать push вместо navigate . Я обновил код в своем ответе. Затем он сохраняет историю того, где вы были.

4. Я думаю, что нашел исправление для этого, но это довольно сложно. Итак, в основном вам нужно отслеживать текущее состояние навигации ExampleNavigator и проверять, есть ли у вас уже маршрут в состоянии с определенной комбинацией routeName и userId . Если вы уже это сделали, вы вернетесь к первой комбинации, которую вы нашли по ее ключу.

5. Спасибо, но я исправил проблему, разрешив пользователю нажимать кнопку только один раз. Вы мне очень помогли!