React Router render components with /:username or /component/ - reactjs

I am trying to implement a routing structure where a user goes to another user's page or their own when the path is /:username. I also want to render another page with a path /watch or /watch/ .. Facebook has a similar setup where /:username will take you to your page or another user's and /watch/ for example is a page. Is there best practice to achieve this with react-router?
As of now I have something like this..
<Route path="/" exact component={authenticated ? Home : Index} />
<Route path="/watch/" component={Watch} />
<Route path="/:username" exact component={({match}) => {
if(match.params.username === data.Username) {
return <ProfilePage match={match} />
} else {
return <UserPage match={match} />
}
}} />
Now if I got to /watch/ the profile component is being rendered aswell. So :username is going to match all my routes?

As you already deducted, /:username is matching at the same time as /watch/ because both patterns match the URL /watch/.
Thankfully, React Router provides a <Switch> component for cases like this one, where only the first match is rendered:
<Switch>
<Route path="/watch/" component={Watch} />
<Route path="/:username" component={...} />
</Switch>
Now, with the URL /watch/, only the first route is rendered, even though the second one matches too.

If you are using react-router-dom v6, do these:
instead of Switch, you should use Routes
instead of component={<SomeComponent />} property, use element={<SomeComponent />}
Just in case, you can Read this article about upgrading from v5 to v6

Related

Redirecting to subpage with id

I've had two routes:
<Route path="/client/:id/members" component={Members} />
<Route path="/client/:id/members/:mid" component={MemberProfile} />
why when I'm trigger:
window.location.href = `/client/${id}/members/${mid}`;
then my url is changed to correct form like in the route path but not redirect me to MemberProfile component?
Thanks for any help!
as
<Route path="/client/:id/members" component={Members} />
declared before
<Route path="/client/:id/members/:mid" component={MemberProfile} />
and
/client/${id}/members/${mid}
fit
"/client/:id/members"
Members component will still be rendered.
Consider one of the following:
decleare MemberProfile before Members
change MembersProfile route to /client/:id/member/:mid for example
use exact Route property
Given:
<Route path="/client/:id/members" component={Members} />
<Route path="/client/:id/members/:mid" component={MemberProfile} />
It seems like you are rendering these routes into a Switch component. Remember that the Switch component renders the first child <Route> or <Redirect> that matches the location. This means that route path order and specificity matters.
Order your routes by decreasing specificity. "/client/:id/members/:mid" is more specific than "/client/:id/members" and should render higher in the Switch.
<Switch>
...
<Route path="/client/:id/members/:mid" component={MemberProfile} />
<Route path="/client/:id/members" component={Members} />
...
</Switch>
Additional note
You may want to avoid using window.location.href to redirect as this reloads the entire page, and thus, your app. Use a Redirect component to render a declarative navigation, or use history.replace(`/client/${id}/members/${mid}`); to issue an imperative redirect.

React Router - page is not re-rendered at route change

I have a simple app where I try to interchange between 2 routes, and for each route there is a specific component to be rendered. Unfortunately, it seems like it remains stuck on the first Route I set inside the Switch.
Here you can access a sandbox containing the simplified app. I have set 2 NavLink components to manage the 2 routes.
in the builer component, you should use path instead to :
<Route path="/checkout" exact render={() => <div>Checkout</div>} />
<Route path="/" exact>
{this.state.showModal ? <div>Modal {orderSummary}</div> : null}
<div>Main Page</div>
{this.state.error ? errorMessage : ingredientControls}
</Route>
I tried it, it's work

Is there such a thing like sass mixins in react?

So I have a react application that involves a lot of routing and this routings are done based on conditions. For example if a user logs in then some of the paths might mean something different than what they would mean if the user wasn't logged in. Here is a simple example.
<Switch>
(this.state.isAuthenticated)?
this.state.game?
<Route path="/game/Game91" render={(props) => <Game91 {...props} games={this.state.allGames} />} />:
<Route path="/game/Game91/" render={(props) => <CreateGame {...props} game="game91" />} />:
<Route exact path="/game/Game91" render={(props) => <LoginForm {...props} onGuestLogin = {this.guestLogin} parentLocation = "/game/Game91" />} />
}
</Switch>
...
So in the above code if a user is authenticated and if the game is choosen then I want the url /game/Game91 to take the user to the Game91 component. If the user is authenticated but a game isnt choosen I want /game/Game91 to take the user to the create game component and so forth. And this has been workign for me for now. I unserstand this might not be the best practice out their, so I would be glad if you share what the best practices are in such scinarios and send me links to more reads. But my main question for now is that what if I have another path like /somepath/to that also means different things like the /game/Game91. To do, that I will have to rewrite all the above code since I can't fit all this in one ternary operator. Hypotetically this is what I would like.
<Switch>
(this.state.isAuthenticated)?
this.state.game?
<Route path="/game/Game91" render={..something..} />
<Route path="/somePath/to" render={..something..} />
:
<Route path="/game/Game91" render={..somethingElse..} />
<Route path="/somePath/to" render={..somethingElse..} />
:
<Route path="/game/Game91" render={..something..} />
<Route path="/somePath/to" render={..something..} />
}
</Switch>
But the above isn't possible because a ternary operators can't return more than one thing. and I cant use if statments beacuse JSX doesn't allow them. So ideally if I could have function that will return this pair of things without wrapping them in any kind of container like you would with scss mixins that would be great. So is there such a thing in react or are there other better ways to do such things?
Generally every page or container should be addressed to only one route path. Instead of changing the component at runtime by checking the flags, you can navigate from one route to another based on your conditions using react hooks.
Note:
This example uses react router v6. If you are using the v5.2 version of react router, then you need to use history api instead of navigate.
useEffect(() => {
if (isAuthenticated) {
if(isGameActive)
navigate('/gaming');
else
navigate('/create-game');
}
else {
navigate('/login');
}
}, [isAuthenticated]);
<Router>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/gaming" element={<Game91 />} />
<Route path="/create-game" element={<CreateGaming />} />
</Routes>
</Router>
react-router v6

nested routes with optional parameter

Hi there for localization reasion i need optional nested routes like /en/mypage , /ru/mypage , /my page, I already tried to work with <Route but didn't get it working.
<Route path="/:lang(en|lt|ru)?" >
<Route path="/surveys" exact component={Dashboard} />
</Route>
Already found solutions above with earlier versions but it doesn't seem working now. How can i implement it? i know it's posible to add to every route parameter, but isn't here more elegant way.
You can't nest your routes in React-Router v4. Instead you should add nested routes in component or use render prop:
<Route path="/:lang(en|lt|ru)" render={({ match: { url } }) => (
<>
<Route path={`${url}/surveys`} component={Dashboard} exact />
</>
)}
/>
In above example your available routes would be
/en/surveys
/lt/surveys
/ru/surveys

location.pathname vs router

I am still learning react router v4. and I'd like to know what exactly is the difference between doing this
getting location using
const {
match,
location,
layoutBoxed,
navCollapsed,
navBehind,
fixedHeader,
sidebarWidth,
theme,
} = this.props;
Option 1
if(location.pathname === '/500'){
return <Page500 />
and this
Option 2
<Route path={'/500'} component={Page500} />
As for me, while the first option displays everything properly for me,
the 2nd one i.e. the router one, shows the component in only half the page.
Now, why is that happening?
Option 1 result -->
Option 2 result -->
But the main point--> what is the difference between using location.path name and router
In Option 2 <Route path={'/500'} component={Page500} />
Here you are creating a Route which has a path of /500 and loads a component Page500. This means that when the user navigates to the path specified in the Route, React-Router will render the component where the Route was defined.
In Option 1 ,
if(location.pathname === '/500'){
return <Page500 />
}
the parent component decides when to render the Page500 component, based on the location prop which it receives. This location prop would ultimately be coming from a Route or the withRouter HOC. This is equivalent to
<Route render={(props)=>{
if(props.location.pathname === '/500'){
return <Page500 />;
}else{
return null;
}
}
}
/>
which can also be written as
<Route path={'/500'} component={Page500} />
So to sum it up, you can only do Option 1 if you get the location prop from the parent component, you can define a Route (Option 2) anywhere in the application.
Edit:
If you have all your Routes like
return( <div>
<Route path={'/500'} component={Page500} />
<Route path={'/confirm-email'} component={PageConfirmEmail} />
<Route path={'/lock-screen'} component={PageLockScreen} />
<Route path={'/login'} component={PageLogin} />
<Route path={'/sign-up'} component={PageSignUp} />
<Route path={'/forgot-password'} component={PageForgotPassword} />
<Route path={'/fullscreen'} component={PageFullscreen} />
</div> );
you are running the risk of multiple Routes rendering which might be why you are getting half page render in Option 2. To prevent that from happening and only render the first Route that matches, you should add a Switch
return( <div>
<Switch>
<Route path={'/500'} component={Page500} />
<Route path={'/confirm-email'} component={PageConfirmEmail} />
<Route path={'/lock-screen'} component={PageLockScreen} />
<Route path={'/login'} component={PageLogin} />
<Route path={'/sign-up'} component={PageSignUp} />
<Route path={'/forgot-password'} component={PageForgotPassword} />
<Route path={'/fullscreen'} component={PageFullscreen} />
</Switch>
</div> );
More on Switch can be found at https://reacttraining.com/react-router/web/api/Switch
One of the main features with react router is that you can do stuff such as:
<Route path="/user/:id" component={User} />
and id will be passed into User component.
Ex. /user/bob and /user/jill will both render a User component, but in your componentDidMount you can now fetch the correct user information from your API.
With location.pathname that task becomes more convoluted as #palsrealm mentioned. But first of all, the location prop must be available for this method to work.
There are other features that you would be losing out, but that is the main one I can think of so far. You can check out the Route api documentation here.
EDIT: As for why it is rendering differently, I really can't answer without more context. For example, is the Route wrapped in a Switch component like so:
<Switch>
// Other routes
<Route exact path="/500" component={Page500} />
</Switch>

Resources