Previous screen called when navigating to new one in React Native

Viewed 1871

I have the exact same problem as described here. When navigating between screens using Tabs or a drawer, each screen is reloaded, causing issues with my app.

I have tried to follow some of the recommendations given in the thread, but I can't apply it with my code. E.g.:

For me the solution was to nest the navigators as child of the screen component and not inserting the navigator into the component property.

Wrap it in the top level of the file and assign it to a variable, then use that variable inside your component.

My drawer is composed of 4 shortcuts to 4 screens (Home, History, Groups, Settings). The first three are also available from the Home screen in bottom-tabs.

Here is my code: Navigation.js

import React from "react";
import { TouchableOpacity } from "react-native";
import { NavigationContainer} from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { createDrawerNavigator } from "@react-navigation/drawer";

import { Entypo } from "@expo/vector-icons";

import Settings from "../screens/Settings";
import Loading from "../screens/Loading";
import AddGroup from "../screens/AddGroup";
import GroupList from "../screens/GroupList";

import Home from "../screens/Home";
import Groups from "../screens/Groups";
import History from "../screens/History";

const HomeStack = createStackNavigator();
const HomeStackScreen = ({ route }) => (
  <HomeStack.Navigator}>
    <HomeStack.Screen
      name={route.name}
      component={Home}
    />
  </HomeStack.Navigator>
);

const HistoryStack = createStackNavigator();
const HistoryStackScreen = ({ route }) => (
  <HistoryStack.Navigator>
    <HistoryStack.Screen
      name={route.name}
      component={History}
    />
  </HistoryStack.Navigator>
);

const GroupsStack = createStackNavigator();
const GroupsStackScreen = ({ route }) => (
  <GroupsStack.Navigator>
    <GroupsStack.Screen
      name={route.name}
      component={Groups}
    />
  </GroupsStack.Navigator>
);

const AppTabs = createBottomTabNavigator();
const bottomTabsScreen = ({ route }) => (
  <AppTabs.Navigator>
    <AppTabs.Screen
      name="Home"
      component={HomeStackScreen}
    />
    <AppTabs.Screen
      name="History"
      component={HistoryStackScreen}
    />

    <AppTabs.Screen
      name="Groups"
      component={GroupsStackScreen}
    />
  </AppTabs.Navigator>
);

const AppDrawer = createDrawerNavigator();
const AppDrawerScreen = ({ navigation }) => (
  <AppDrawer.Navigator>
    <AppDrawer.Screen name="Home" component={bottomTabsScreen} />

    <AppDrawer.Screen name="History" component={HistoryStackScreen} />

    <AppDrawer.Screen name="Groups" component={GroupsStackScreen} />

    <AppDrawer.Screen name="Settings" component={Settings} />
  </AppDrawer.Navigator>
);

const RootStack = createStackNavigator();
const RootStackScreen = () => {
  const [isLoading, setIsLoading] = React.useState(true);

  React.useEffect(() => {
    setTimeout(() => {
      setIsLoading(!isLoading);
    }, 500);
  }, []);

  return (
    <RootStack.Navigator mode="modal">
      {isLoading ? (
        <RootStack.Screen name="Loading" component={Loading} />
      ) : (
        <RootStack.Screen
          name="AppDrawerScreen"
          component={AppDrawerScreen}
        />
      )}
      <RootStack.Screen
        name="AddGroup"
        component={AddGroup}
        options={modalOptionStyle}
      />

      <RootStack.Screen
        name="GroupList"
        component={GroupList}
        options={({ navigation }) => ({
          headerTitle: "Select an existing group",
        })}
      />
    </RootStack.Navigator>
  );
};

export default () => {
  return (
    <NavigationContainer>
      <RootStackScreen />
    </NavigationContainer>
  );
};

index.js:

import React from "react";
import Navigation from "./config/Navigation";
import { ConversionContextProvider } from "./util/ConversionContext";

export default () => (
  <ConversionContextProvider>
    <Navigation />
  </ConversionContextProvider>
);

Final working code after following x00 recommendations:

useEffect(() => {
  if (isFocusedGroups) setLoading(true);
  const unsubscribe = navigation.addListener("focus", () => {
    loadFlatlist("groups")
      .then()
      .catch((error) => console.error(error))
      .finally(() => {
        setLoading(false);
      });
  });
  return unsubscribe;
}, [navigation, isFocusedGroups]);
1 Answers

Form your repo:

...
import { useIsFocused } from "@react-navigation/native";
...
const History = ({ navigation }) => {
  ...
  const isFocusedHistory = useIsFocused();
  ...
  useEffect(() => {
    ...
    loadFlatlist("savedRuns")
    ...
  }, [isFocusedHistory]);
  ...
}

every time a user opens or closes the tab isFocusedHistory changes, so the corresponding component re-renders and effects get triggered.

Haven't read the whole app you've wrote and haven't tried to understand what's the desired behavior is, so can't say exactly how you need to change it. But that's the root cause of your issue.

Probably, you should ditch the idea of retrieving the data on the tab switch. That's not a great idea anyway.

Related