Search code examples
reactjsionic-frameworkmobiletabsreact-router-dom

Issue with Ionic React Tabs Not Switching Pages Properly


I've been trying to set up a simple tabs navigation in my Ionic React app, linking one page to another, but I can't seem to get it right. I've implemented similar tabs on other pages using the same structure, and they work perfectly. However, for my landing page, the tabs do not work as expected.

This setup works perfectly on other pages:

ionic

const Perfil: React.FC = () => {
  return (
    <IonTabs>
      <IonTabBar slot="bottom">
        <IonTabButton tab="Tab1perfil" href="/menu/perfil/Tab1perfil">
          <IonIcon icon={personOutline} />
          <IonLabel>Perfil</IonLabel>
        </IonTabButton>
        <IonTabButton tab="Tab2perfil" href="/menu/perfil/Tab2perfil">
          <IonIcon icon={settingsOutline} />
          <IonLabel>Configurações</IonLabel>
        </IonTabButton>
      </IonTabBar>
      <IonRouterOutlet>
        <Route path="/menu/perfil/Tab1perfil" component={Tab1perfil} />
        <Route path="/menu/perfil/Tab2perfil" component={Tab2perfil} />
        <Route exact path="/menu/perfil">
          <Redirect to="/menu/perfil/Tab1perfil" />
        </Route>
      </IonRouterOutlet>
    </IonTabs>
  );
};

This is the code for my landing page, which does not work as expected:

ionic

const Inicial: React.FC = () => {
  return (
    <IonTabs>
      <IonTabBar color={"dark"} slot="bottom">
        <IonTabButton tab="Ofertas" href="/menu/Inicial/Ofertas">
          <IonIcon color="tbpink" icon={pricetagOutline} />
          <IonLabel color="tbpink">Ofertas</IonLabel>
        </IonTabButton>
        <IonTabButton tab="Trending" href="/menu/Inicial/Trending">
          <IonIcon color="tbpink" icon={flameOutline} />
          <IonLabel color="tbpink">Populares</IonLabel>
        </IonTabButton>
      </IonTabBar>
      <IonRouterOutlet>
        <Route path="/menu/Inicial/Trending" component={Trending} exact />
        <Route path="/menu/Inicial/Ofertas" component={Ofertas} exact />
        <Route exact path="/menu/Inicial">
          <Redirect to="/menu/Inicial/Ofertas" />
        </Route>
      </IonRouterOutlet>
    </IonTabs>
  );
};

export default Inicial;

Here is my App.tsx for context:

ionic

const App: React.FC = () => (
  <IonApp>
    <IonReactRouter>
      <IonRouterOutlet>
        <Route exact path="/">
          <Login />
        </Route>
        <Route path="/register" component={Register} exact />
        <Route path="/login">
          <Login />
        </Route>
        <Route path="/menu" component={Menu} />
        <Route path="/Trending" component={Trending} />    
      </IonRouterOutlet>
    </IonReactRouter>
  </IonApp>
);

export default App;

Login function for context:

ionic

const [present, dismiss] = useIonLoading();
const router = useIonRouter();

const DoLogin = async (event: any) => {
  event.preventDefault();
  await present("Fazendo Login...");
  setTimeout(async () => {
    dismiss();
    router.push("/menu", "root");
  }, 200);
  console.log("DoLogin");
};

My menu component:

ionic

const Menu: React.FC = () => {
  const paths = [
    { name: "Inicial", url: "/menu/inicial", icon: homeOutline },
    { name: "Perfil", url: "/menu/perfil", icon: personOutline },
    { name: "Carrinho", url: "/menu/carrinho", icon: cartOutline },
    { name: "Configurações", url: "/menu/settings", icon: settingsOutline },
  ];

  return (
    <IonPage color={"dark"}>
      <IonContent color={"dark"}></IonContent>
      <IonMenu color="tbpurple" contentId="main">
        <IonHeader color="tbpink">
          <IonToolbar color={"dark"}>
            <IonTitle color="tbpink">Menu</IonTitle>
          </IonToolbar>
        </IonHeader>
        <IonContent color="dark" className="ion-padding">
          {paths.map((item, index) => (
            <IonMenuToggle key={index}>
              <IonItem
                color={"dark"}
                routerLink={item.url}
                routerDirection="none"
              >
                <IonIcon slot="start" color="tbpink" icon={item.icon} />
                <IonText color={"tbpink"}>{item.name}</IonText>
              </IonItem>
            </IonMenuToggle>
          ))}
        </IonContent>
      </IonMenu>
      <IonRouterOutlet id="main">
        <Route exact path="/menu/Inicial" component={Inicial} />
        <Route path="/menu/settings" component={Settings} />
        <Route path="/menu/perfil" component={Perfil} />
        <Route exact path="/menu">
          <Redirect to="/menu/Inicial" />
        </Route>
      </IonRouterOutlet>
    </IonPage>
  );
};

export default Menu;

Please help, I'm going insane and I can't figure this out. Sorry for the spelling mistakes, English is not my first language :)


Solution

  • The issue is in the Menu component, the route rendering the Inicial component can only exactly match "/menu/inicial", which will obviously exclude all sub-routes, e.g. "/menu/incial/trending" and "/menu/incial/Ofertas" cannot be matched and rendered.

    <IonRouterOutlet id="main">
      <Route
        exact                // <-- problem!
        path="/menu/Inicial" // <-- only exactly this path can match!
        component={Inicial}
      />
      <Route path="/menu/settings" component={Settings} />
      <Route path="/menu/perfil" component={Perfil} />
      <Route exact path="/menu">
        <Redirect to="/menu/Inicial" />
      </Route>
    </IonRouterOutlet>
    

    Remove the exact prop from the Inicial route so any routes with "/menu/inicial" as a path prefix can also be matched and rendered.

    <IonRouterOutlet id="main">
      <Route
        path="/menu/Inicial" // <-- "/menu/inicial/*"
        component={Inicial}
      />
      <Route path="/menu/settings" component={Settings} />
      <Route path="/menu/perfil" component={Perfil} />
      <Redirect exact from="/menu" to="/menu/Inicial" />
    </IonRouterOutlet>