I have the following routes defined:
<Switch>
<Route path="/" exact component={HomePage} />
<Route path="/project/:id/search" component={SearchPage} />
<Route path="/project/:id/upload" component={UploadPage} />
</Switch>
The issue is that search results (local state of the SearchPage component) remain when you navigate to another project's search page. I could solve it by manually clearing state in willReceiveProps when the ID changes but I want to avoid that as there are several sub-pages of /project that would need the logic.
Using render={()=> <SearchPage />} made no difference.
What's a clean way of ensuring that my components get re-initialized when the parameter in the URL changes?
You may use 'key reset state' technique: if special prop key is different React will for sure recreate element instead of updating.
So it may be
<Route
path="/project/:id/search"
render={({ match: {params: {id} } })=> <SearchPage key={id} />}
/>
To me this affects readability in bad way. So using componentDidUpdate(willReceiveProps is deprecated) seems to be better maintainable.
Related
I have some React routes that when I nest one route inside another, I need to repeat the route path.
To explain, for example:
<Route
path="admin">
<Switch>
<Route
path="admin/specific/:id"
component={SpecificAdmin} />
<Route
exact
path="admin"
component={AdminPage}>
</Route>
<Route
exact
path="admin/edit/new"
component={EditSpecificAdmin} />
</Switch>
</Route>
I want a page where I can see the list of items, one for adding a new one and another for looking, editing a specific item. So I thought about the paths edit/new and specific/1.
So the routes do not detect when I write specific/1 (the specific id) and not either the admin nesting, so I need to write the admin in each one...
As Tareq aziz said, you can easily have intel in props.
You can create another router to pass easily new value:
// your original component
import AdminRouter from './Admin/Router';
export default () => {
return (
<Route path="admin">
<AdminRouter />
</Route>
);
}
// in ./Admin/Router.js
export default (props) => {
return (
<Switch>
<Route
exact
path={`${props.match.path}/specific/:id`}
component={SpecificAdmin}
/>
<Route
exact
path={`${props.match.path}`}
component={AdminPage}
/>
<Route
exact
path={`${props.match.path}/edit/new`}
component={EditSpecificAdmin}
/>
</Switch>
);
}
I'm not sure though if the order of the routes are correct.
I think you can get your current page's url from props using location.pathname or match.url. You can see my image. Then you may add your nested route after that. Hope it will help you
You may code your path like this way
path=`${this.props.location.pathname}/edit/new`
path=`${this.props.location.pathname}/specific/:id`
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 need a View component and an Edit component. Depends on conditions, a button needs to go View page or Edit page.
I set a Route like below.
The problem is the paths are the same.
<Route exact path='/users/:user_id' component={Users.Edit} />
<Route exact path='/users/:user_id' component={Users.View} />
In page, I have links like below
(type = "A") ? <Link to="users/1">view</Link> : <Link to="users/1">edit</Link>
For now, the router has same path so the button goes to the Edit component.
Is there any good react way I can call Edit component or View component?
or Do I need to just like below?
<Route exact path='/users/:user_id' component={Users.Edit} />
<Route exact path='/users/:user_id/view' component={Users.View} />
I think this is the right decision
<Route exact path='/users/:user_id' component={Users.Edit} />
<Route exact path='/users/:user_id/view' component={Users.View} />
But usual practice for CRUD to define routes in this way
<Route exact path='/users/' component={Users.List} />
<Route exact path='/users/create' component={Users.Create} />
<Route exact path='/users/:user_id' component={Users.View} />
<Route exact path='/users/edit/:user_id' component={Users.Edit} />
You should do it like you suggest with the /view suffix, but I would suggest combining the /users/:user_id with the Users.View component, and the /users/:user_id/edit with the Users.Edit component.
The reason 1 exact url cannot be handled based on conditions is when you open that url from let's say an e-mail, there is no way of telling which component to render.
There is a way, but it's harder than adding just 2 routes.
You might want to change the view/edit decision to a query param on 1 route, but then you have to create a new wrapper component around Users.Edit and Users.View to handle the decision based on the query param. So your route should look like
<Route exact path='/users/:user_id' component={Users.EditOrViewDecision} />
Next you can create links to that route by passing search to the Link
<Link to={{
pathname: '/users/1',
search: '?view=edit'
}}/>
But this way you still have to manage your decision in the EditOrViewDecision based on the ?view=edit query which component you want to display.
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.