3

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]);
2
  • Your code has errors in it. For one thing: modalOptionStyle is not defined. Probably it don't matter but obscures your issue. Same goes for navigation prop in AppDrawerScreen you are not actually using. Etc. Plz remove noise and make a minimal reproducible example (big chance you'll solve your problem yourself in the process). Also you should show some screens' code and the way you navigate. Also inline function options={({ navigation }) => ({ headerTitle: "Select an existing group" })} can cause some rerenders, but probably only for GroupList. Anyway, we need more code Commented Feb 27, 2021 at 9:09
  • I've created a repro here, I will try to clean it up later today with minimum code. When switching between History and Groups tab, you can see in the console "LOADING FLATLIST (loadFlatlist) FOR: savedRuns" executed each time. I think that's the root cause of the problem. Commented Feb 27, 2021 at 13:45

1 Answer 1

2
+100

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.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, I removed that but the problem is that the data in the tab does not get refreshed when I move tab. Why isn't that a great idea? If a user saves a run, and goes to History, then back to Home to save another run, I want the second run to be displayed in the flatlist in History if the user navigates there (i.e. call loadFlatlist again). I was under the impression that useIsFocused would help me to do that.
You can use some kind of store (Redux, MobX, etc), you can pass data as props, or in a context. But if you want to request data on tab switch that will only happen along with re-render. useEffect wont magically run in the background (unless you'll put setInterval in there)
Well, I still don't know the desired behavior. But you can run useEffect only when a tab is shown (and not when it's hidden). For that you can simply put if(isFocusedHistory) inside of useEffect
Thanks, I eventually managed to solve it. Added the working sample in my original question

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.