Hi all, I am using Ionic React Tabs for a music application. I have a quick question on how to handle an IonRouterOutlet
for tabs that share the same page component across tabs. For example, my application has two pages:
On both the Search
and Feed
pages, I need to allow a user to route to an Artist
page. According to the documentation, I should be making my IonRouterOutlet
look something like the following in my App.tsx
:
<IonRouterOutlet>
<Route path="/:tab(search)" component={Search} exact />
<Route path="/:tab(search)/artist/:artistId" component={Artist} exact />
<Route path="/:tab(feed)" component={Feed} exact />
<Route path="/:tab(feed)/artist/:artistId" component={Artist} exact />
</IonRouterOutlet>
This is manageable so far, but now on each Artist
page, a user can click an Album
so I’d have to add another Route
for both the Search
and Feed
tabs to account for the ability to route to /(whatever-tab)/album/:albumId
. This is getting quickly out of hand because I have several shared components that are used amongst several tabs (4 tabs in my actual app). Is there a better way to handle this?
As mentioned in the docs, path
simply uses regex to match the url. Is there a way to do something like:
<IonRouterOutlet>
<Route path="/:tab(search)" component={Search} exact />
<Route path="/:tab(feed)" component={Feed} exact />
<!-- updated the following line --->
<Route path="/:tab(search || feed)/artist/:artistId" component={Artist} exact />
</IonRouterOutlet>
So it can match several tabs based on what I pass as the current tab from the match
props to my routerLink
?
What’s the best practice here? The example tabs app doesn’t even use this :tab()
syntax…is this out of date now? Thanks!
So you want the Artist page to show up under the search tab or the feed tab depending on where the user navigates from? Sounds like the Artist page doesn’t really have its own tab. Have you tried simply not setting one, i.e. mapping the page to /artist/:artistId
?
In any case, Route
is from React Router and path is documented there.
<IonApp>
<IonReactRouter>
<IonTabs>
<IonRouterOutlet>
<Route path="/tab1" component={Tab1} exact={true} />
<Route path="/commonTab/:tab" component={CommonTab} exact={true} />
<Route path="/commonTab/details" component={Details} />
<Route path="/tab4" component={Tab4} />
<Route exact path="/" render={() => <Redirect to="/tab1" />} />
</IonRouterOutlet>
<IonTabBar slot="bottom" style={tabBarStyle}>
<IonTabButton tab="tab1" href="/tab1">
<IonIcon icon={flash} />
<IonLabel>Tab One</IonLabel>
</IonTabButton>
<IonTabButton tab="tab2" href="/commonTab/tab2">
<IonIcon icon={apps} />
<IonLabel>Tab Two</IonLabel>
</IonTabButton>
<IonTabButton tab="tab3" href="/commonTab/tab3">
<IonIcon icon={apps} />
<IonLabel>Tab Three</IonLabel>
</IonTabButton>
<IonTabButton tab="tab4" href="/tab4">
<IonIcon icon={send} />
<IonLabel>Tab Four</IonLabel>
</IonTabButton>
</IonTabBar>
</IonTabs>
</IonReactRouter>
</IonApp>
const CommonTab = () => {
const history = useHistory();
const params = useParams();
console.log(params);
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle>{params.tab === "tab2" ? "Tab 2" : "Tab 3"}</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<IonList>
<IonItem
onClick={() => {
history.push("commonTab/details");
}}
>
<IonLabel>
<h2>Go to detail</h2>
<p>{JSON.stringify(params)}</p>
</IonLabel>
</IonItem>
</IonList>
</IonContent>
</IonPage>
);
};
export default CommonTab;
3 Likes
Hey @aaronksaunders I was actually looking at your Dev.to site last night and saw your comment about how :path
had changed and to look at your newer projects for the updated method, but couldn’t find anything! I guess I didn’t convey my question properly. It looks like what you commented has to do with a shared base tab page? In my code I have certain page components that are used across several tabs after they press on a certain tab. For example:
User clicks on Search
Tab -> they search for an artist -> they tap to go to Artist
page
Now user clicks on Feed
tab -> there is an artist in their feed -> they tap to go to Artist
page
(Notice how the component that is the same is a subroute of the tab they’re in?)
Currently, I have my routing set up like this:
{/* Feed */}
<Route path="/:tab(feed)" component={Home} />
<!-- Indentical to two routes in SEARCH except for :tab(search) prefix -->
<Route path="/:tab(feed)/artist/:artistId" component={Artist} exact />
<Route path="/:tab(feed)/album/:albumId" component={Album} exact />
{/* Search */}
<Route path="/:tab(search)" component={Search} exact />
<!-- Indentical to two routes in FEED except for :tab(search) prefix -->
<Route path="/:tab(search)/artist/:artistId" component={Artist} exact />
<Route path="/:tab(search)/album/:albumId" component={Album} exact />
Am I interpreting what you posted incorrectly? It just feels odd having to duplicate code like this per-tab.
@mirkonasato you would be correct! Artist
isn’t a tab at the bottom per-say. However the solution to just have a Route
called artist/:artistId
that looks something like this will present an issue:
<Route path="/artist/:artistId" component={Artist} exact />
The issue here is that if I’m on the Search
tab, and I route to an Artist
page using react-router-dom
like this:
<Link to={`/artist/${artistId}`} />
This routes me to the Artist
page while Search
is still highlighted as the active tab at the bottom. However, if I were to tap over to a different tab (like the Feed
tab) and then tap back to the Search
tab. My current state is removed and I see the base Search
page again instead of seeing the Artist
on the Search
page that I routed to.
Here’s a streamable that shows me doing the following: https://streamable.com/5o1zf0
- Viewing the
Search
tab
- searching for an artist and routing to
artist/:artistId
as you suggested
- clicking over to
Feed
tab
- routing back to
Search
and the artist I clicked on (Alison Wonderland) is not showing on that tab anymore
If I add corresponding :tab(search)/artist/:artistId
or :tab(feed)/artist/:artistId
then the tabs maintain their page state when clicking between them (which is desired)
Sorry I read the question wrong, but the same concept can be used to duplicate the detail page - see updated example - there is no duplicated code - https://stackblitz.com/edit/ionic-react-tabs-txjljk
I would strongly suggest you look in to using a better way to manage state than passing information around as url parameters, it will make it easier to test the components
@aaronksaunders thanks so far! This app will be deployed on web too, so passing the Artists Id seems logical since the url’s can be sharable and if a user pastes /artist/12
in to the browser url they should be routed to that specific artists’ page. What other methods do you suggest?
Interesting. You can pass an array with multiple values as the path if you want to.
Incidentally, React Router recommends using children elements these days rather than the component
prop.
I think the solution I provided should solve your problem. I was just making a suggestion about the parameters; if it is web based, I think you are good
Thanks for that suggestion… will look into that approach also.
I like to emphasize readability and user simplicity in examples - lots of the people asking questions here, aren’t really advanced developers… they are just looking to get something working
Really its good I will try it. Thanks for your suggestion