0
votes

If I have a screen that receives route params, does some processing, and then re-routes to a sub-screen, this works if the screen was previously mounted but I get the following error if I try this after the first componentDidMount:

The action 'NAVIGATE' with payload {"name":"Chat","params":{"name":"Person2"}} was not handled by any navigator.

Do you have a screen named 'Chat'?

If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.

This is a development-only warning and won't be shown in production.
[...]

Here are the highlights:

  1. Tab navigator (App) with home (HomeScreen) and chats (ChatsScreenStack) tabs.
  2. The chats tab is a stack navigator with a chats list to list all chats (ChatsListScreen) and a chat screen to show a particular chat (ChatScreen).
  3. The chats tab stack navigator (ChatsScreenStack) has a componentDidUpdate which checks if the name prop has been updated, and, if so, it navigates to the chat tab.
  4. The chats tab stack navigator also has a constructor which checks if it was created with a name prop, and, if so, it saves it off to a field and does the same navigation as above in componentDidMount.

Item 3 works but Item 4 doesn't work. Is this because react-navigation hasn't built up its navigation state at the time of the first componentDidMount? If so, how do I get a callback when react-navigation is ready?

Below is a reproduction (Snack link, Github link). If you launch, and click on ChatsTab, click back on HomeTab, and then click on the button it works. However, if you launch, and immediately click on the HomeTab button, it gives the error (in development mode; on the snack, it will navigate to the chats list rather than the chat screen).

import * as React from 'react';
import { Button, FlatList, Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createStackNavigator } from '@react-navigation/stack';

class ChatScreen extends React.Component {
  render() {
    return (
      <View style={{ padding: 10 }}>
        <Text>Chat with {this.props.route.params.name}</Text>
      </View>
    );
  }
}

class ChatsListScreen extends React.Component {
  render() {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <FlatList
          data={[ {name: "Person1", key: "1"}, {name: "Person2", key: "2"}]}
          renderItem={(data) => {
            return (
              <View key={data.item.key} style={{ margin: 10 }}>
                <Button
                  title={data.item.name}
                  onPress={() => this.props.navigation.navigate("Chat", { name: data.item.name })}
                />
              </View>
            );
          }}
        />
      </View>
    );
  }
}

const ChatsStack = createStackNavigator();

class ChatsScreenStack extends React.Component {
  constructor(props) {
    super(props);

    if (props.route && props.route.params && props.route.params.name) {
      this.pendingReroute = props.route.params.name;
    }
  }

  componentDidMount() {
    if (this.pendingReroute) {
      this.props.navigation.navigate("Chat", { name: this.pendingReroute });
    }
  }

  componentDidUpdate(prevProps) {
    let updated = false;
    if (this.props.route && this.props.route.params.name) {
      updated = true;
      if (prevProps.route && prevProps.route.params && prevProps.route.params.name == this.props.route.params.name) {
        updated = false;
      }
    }
    if (updated) {
      this.props.navigation.navigate("Chat", { name: this.props.route.params.name });
    }
  }

  render() {
    return (
      <ChatsStack.Navigator>
        <ChatsStack.Screen name="Chats" component={ChatsListScreen} />
        <ChatsStack.Screen name="Chat" component={ChatScreen} />
      </ChatsStack.Navigator>
    );
  }
}

class HomeScreen extends React.Component {
  render() {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Button
          title="Navigate to Person2"
          onPress={() => this.props.navigation.navigate("ChatsTab", { name: "Person2" })}
        />
      </View>
    );
  }
}

const Tabs = createBottomTabNavigator();

export default class App extends React.Component {
  render() {
    return (
      <NavigationContainer>
        <Tabs.Navigator>
          <Tabs.Screen name="HomeTab" component={HomeScreen} />
          <Tabs.Screen name="ChatsTab" component={ChatsScreenStack} />
        </Tabs.Navigator>
      </NavigationContainer>
    );
  }
}
1

1 Answers

0
votes

Yes, this was related to react-navigation not being ready in componentDidMount. I needed to handle the focus event:

class ChatsScreenStack extends React.Component {
  constructor(props) {
    super(props);

    if (props.route && props.route.params && props.route.params.name) {
      this.pendingReroute = props.route.params.name;
    }
  }

  componentDidMount() {
    this.props.navigation.addListener(
      "focus",
      this.onFocus
    );
  }

  onFocus = () => {
    if (this.pendingReroute) {
      const name = this.pendingReroute;
      this.pendingReroute = null;
      this.props.navigation.navigate("Chat", { name: this.pendingReroute });
    }
  }

  [...]