I have a route
<Route exact path="/view/:personID" component={PersonView} />
It works fine, however /view still renders, albeit with no person in it. Is there a way to disallow this path or turn in into a 404?
Answer was to use switch
<Switch>
<Route exact path="/view/:personID" component={PersonView} />
<Redirect from='/view' to='/' />
</Switch>
You can manage this on your component, in the render section test if you have your personID if not return a custom error/view.
Related
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
I have a React Route with significant user input that I want to use the Prompt component with. I'm importing it from react-router-dom without error and adding the component to the render method of the class component, but no matter what, the prompt alert never actually appears and the user can freely navigate off the page without seeing any alert.
I've tried:
Putting the component in with when={aStateBooleanVariable} as well as when={true} and no when attribute at all.
Using a function for the message.
Ensuring that the Route it's on is an exact path.
Importing it from react-router instead of react-router-dom.
Moving the component around within the render method.
Moving the Prompt into the actual Switch statement itself in the App component that wraps everything in BrowserRouter.
Nothing I've tried has gotten the prompt to actually show up or prevent the user from navigating off the page and I've been unable to find anything in the docs or extensive searching where anyone else has had this problem. Does anyone have any idea why this just isn't even starting to work?
I'm not really sure what code to put since it's all pretty much boilerplate react-router-dom.
This is my App.jsx switch statement:
return (
<BrowserRouter>
<header>
{loggedInUser ? <NavbarComp loggedInUser={loggedInUser} setLoggedInUser={setLoggedInUser} /> : null}
</header>
<Switch>
<PrivateRoute path="/ged/campaigns/new" component={CampaignNew} loggedInUser={loggedInUser} />} />
<PrivateRoute path="/ged/campaigns/:id" component={Campaign} loggedInUser={loggedInUser} />} />
<PrivateRoute path="/ged/characters/new" component={CharGen} loggedInUser={loggedInUser} />} />
<PrivateRoute path="/ged/characters/:id" component={CharacterMain} loggedInUser={loggedInUser} />} />
<PrivateRoute path="/ged" component={GEDHome} loggedInUser={loggedInUser} />} />
<Route path="/" component={Home} loggedInUser={loggedInUser} setLoggedInUser={setLoggedInUser} />} />
<Route component={DeadPage} />
</Switch>
</BrowserRouter>
)
I'm importing the component as:
import {Prompt} from 'react-router-dom';
And incorporating the component at the top of its parent's render method return:
<Prompt message="You have changes that will be lost if you leave without saving." />
What other code might be making it fail silently like this?
I know this issue has to do with a fundamental misunderstanding of how React Router works, and more specifically, probably the history object. I have a component called Search, which lets the user search for a particular city. This component appears in multiple places throughout the app, including '/' and '/:cityname'.
From '/', the component works as expected, and correctly pushes the new url param onto the url and the url becomes '/vancouver'. However, from '/vancouver', when I use this same component, the url does not behave as expected. For instance if I enter Istanbul, I am correctly directed to /istanbul, but then as I proceed through the app and click on items, I expect to be directed to '/istanbul/item1'. However, what happens currently is that I end up at '/istanbul/istanbul/item1', which of course is not found, and returns a 404.
Here is the function that gets called when a city is selected (found within Search component)
const onSuggestionSelected = (event, { suggestion }) => {
props.history.push(`/${suggestion.nickname}`)
}
App.js with routes
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/terms-of-service" component={TermsOfServicePage} />
<Route exact path="/privacy-policy" component={PrivacyPolicyPage} />
<Route exact path="/:cityname/personalize" component={FilterPage} />
<Route exact path="/:cityname/experiences" component={SearchPage}>
<Redirect to="/:cityname" />
</Route>
<Route exact path="/:cityname" component={SearchPage} />
<Route exact path="/:cityname/experiences/:experiencename" component={ExperiencePage} />
<Route exact path="/:cityname/experiences/:experiencename/summary" component={(routeProps) => <SummaryPage {...routeProps} />} />
<Route exact path="/:cityname/experiences/:experiencename/payment" component={PaymentPage} />
<Route exact path="/:cityname/experiences/:experiencename/payment-successful" component={PaymentSuccessfulPage} />
<Route component={NotFoundPage} />
<GlobalStyle />
</Switch>
ExploreMore Button
<ButtonWrapper onClick={sendAnalyticsData}>
<LetsGoButton to={{
pathname: `${props.match.params.cityname}/experiences/${experience.nickname}`,
state: props.location.state
}}
palette="tertiary">
Explore more
</LetsGoButton>
</ButtonWrapper>
Please let me know if there is anything else that I can provide that would be helpful. I've tried to do research on how history.push works exactly, but I haven't been able to find much. My best guess is that it takes the current location, and adds on the provided url. Even if that's the case, I can't understand why it would be applying istanbul twice.
I figured out the problem on this one. One of the commenters suggested that I had been using relative paths rather than absolute. I erroneously thought that he was incorrect, seeing as I seemingly have the full url in there. My mistake was to not start off the url with /
before:
${props.match.params.cityname}/experiences/${experience.nickname}
after:
/${props.match.params.cityname}/experiences/${experience.nickname}
I hope this helps someone out.
Here is my Router Implementation
<BrowserRouter>
<div>
<Route exact path="/" component={ProfilesIndex} />
<Route exact path="/profiles" component={ProfilesIndex} />
<Route exact path="/profiles/new" component={ProfileEditor} />
<Route exact path="/profiles/:id" component={ProfileEditor} />
</div>
</BrowserRouter>
When I am browsing to /profiles/new path, it is rendering the ProfileEditor component twice. For every other route, it is working fine.
Can someone suggest how to fix this problem ?
I found the answer after browsing into multiple sections in Router Docs. Problem was it was matching multiple routes
When user opens /profiles/new it matches two routes
/routes/new
/routes/:id
Because :id is like a wildcard * and it also matches new word so both routes get matched and hence the component gets rendered twice.
The solution is to wrap the routes with Switch if you do not want to match multiple routes.
<BrowserRouter>
<Switch>
<Route exact path="/" component={ProfilesIndex} />
<Route exact path="/profiles" component={ProfilesIndex} />
<Route exact path="/profiles/new" component={ProfileEditor} />
<Route exact path="/profiles/:id" component={ProfileEditor} />
</Switch>
</BrowserRouter>
For anyone coming here from v6+
exact prop no longer exists, the paths are exact by default if they aren't appended a wildcard *
However I was still getting a double render. I ran a prod build and checked and the double render is gone so probably nothing to worry about - sometimes hooks run twice in development mode (I guess that's what's happening internally)
for me, this is because of React.StrictNode which is wrapped arround App component.
it intentionally double render components (only in development) to enforce you, not use side effects on some of your component's
lifecycle events.
the reason behind that is documented here
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