React-router-dom / ReactJS beginner in general here.
I've got some working router code that I think can be slightly cleaned up, but attempts at doing so have failed thus far. There are certainly some gaps in my knowledge in this area. I've paged through the similar questions which has helped me expand my understanding, but I haven't successfully connected the dots at this point.
The following code seems to work fine, but I am irked by the repeated "/admin" prefix for all the admin routes.
<BrowserRouter>
<div>
<div className="nav-bar">
<ul>
<li><Link to="/admin">Home</Link></li>
<li><Link to="/admin/content">Content</Link></li>
...
</ul>
</div>
<div className="nav-content">
<Route exact path="/admin" component={AdminHome}/>
<Route path="/admin/content" component={AdminContent}/>
...
</div>
</div>
</BrowserRouter>
However, moving the "nested" routes to sub-route elements doesn't work. The JavaScript console spits out the error message "Warning: You should not use and in the same route; will be ignored."
<Route exact path="/admin" component={AdminHome}>
<Route path="/content" component={AdminContent}/>
...
</Route>
Any suggestions would be greatly appreciated. I've read through a number of SO answers, and found some options in which, for example, I could use <Route path="/admin/:whatever" render={() => (...)}/>, but at this time it doesn't seem like the right path to go down.
The reason for this being, I will ultimately need route parameters further down the tree, e.g. for a URI along the lines of /admin/content/:content_type/:identifier, and ideally my AdminContent component would be agnostic about its parent route match.
Please feel free also to let me know if I'm way off base, and if there is any documentation you believe would show me the light I would love to read it.
Cheers and thanks again!
Thanks to Christopher above for providing a link to some good documentation.
As per the documentation linked, the following changes are now up and running:
{/* top-level route declaration */}
<div className="nav-content">
{/* note the removal of the exact property here */}
<Route path="/admin" component={AdminRoutes}/>
</div>
{/* "sub-routes" (possible poor nomenclature) */}
const AdminRoutes = ({ match }) => (
<div>
{/* note the addition of the exact property here */}
<Route exact path={match.url} component={AdminHome}/>
<Route path={match.url + "/content"} component={AdminContent}/>
<Route path={match.url + "/data-store"} component={AdminDataStore}/>
<Route path={match.url + "/search"} component={AdminSearch}/>
<Route path={match.url + "/communicate"} component={AdminCommunicate}/>
<Route path={match.url + "/promote"} component={AdminPromote}/>
<Route path={match.url + "/measure"} component={AdminMeasure}/>
<Route path={match.url + "/experimental"} component={AdminExperimental}/>
</div>
)
To expand a bit further: in all the examples I've seen so far using nested routes and the inbound "match" parameter, they have been implemented as above, i.e. const X = ({match}) => (...).
But it is possible, and potentially useful, to define a true React.Component subclass, in which the param match is still available via this.props.
class Content extends React.Component {
// usage example
render() {
// `match` is available via inbound props
console.log(this.props.match);
return (
<p>Match is {this.props.match.url}</p>
)
}
}
I'd like to re-iterate at this point that I am a ReactJS beginner... Please do let me know if I'm doing something stupid :)
Related
Im pulling my hair out trying to figure out why the list of react Links is all just loading the error page. Could anyone advise please? its almost a direct copy of a previous project that worked perfectly.
Router Set up
function App() {
return (
<Router>
<Switch>
<Route exact path = '/'>
<Home />
</Route>
<Route path = "*">
<Error />
</Route>
<Route path = '/about'>
<About />
</Route>
<Route path = '/contact'>
<Contact />
</Route>
<Route path = '/deckbuilder'>
<DeckBuilder />
</Route>
</Switch>
</Router>
)
}
export default App;
Links on Home Page
export const Home = () => {
return (
<div>
<h1> Home Page </h1>
<Link to = '/contact'>
Contact
</Link>
<Link to = '/about'>
about
</Link>
<Link to = '*'>
Error
</Link>
<Link to = '/deckbuilder'>
Deck
</Link>
</div>
)
}
You should use react router like this and put error component at last, this is correct syntax:
<Switch>
<Route exact path='/' component={Landing} />
<Route exact path='/sign' component={Sign} />
<Route exact path='/login' component={Dashboard} />
<Route component={GenericNotFound} />
</Switch>
I'll add some more information to the #Shivam Jha answer.
worked, strange as this didnt happen on my last project at all
There's no way to work this on previous project if your router configuration is same like above. This issue in not specific to the React router. Even when developing backend applications this can happen.
The thing which make the problem is that start(*) mark. This mark represent any value. By that mean, the second router declartion of your code, i.e route for <Error /> triggered every time when the url is match to *.
That mean,
/abcd
/sign
/login
All of the above paths match to *. So React router does not even check the next router declarations. It simply route to the <Error /> page. So as the solution, you should always declare static routes at the top of the configuration while things like *, :id defined at bottom
Another case is, If you have, two routes as
abcd.com/posts
abcd.com/:id
then If you defined the abcd.com/:id above the abcd.com/poststhen you will never able to send a request to the /posts endpoint. This is not only specific to React router. Even when doing backend development, you have to be aware of this.
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.
I have a problem when trying to hide the navbar on my login page but i don't know how to do this.
You can see my code here:
render() {
return (
<Router>
<div >
<Nav />
<button type="button" className="form-submit_logout" onClick=
{this.handleLogout.bind(this)}>Logout</button>
<Route path="/" exact component={Login}/>
<Route path="/ChooseRole" exact component={ChooseRole}/>
<Route path="/DashboardGeek" exact component= .
{DashboardGeek}/>
<Route path="/DashboardAdmin" exact component= .
{DashboardAdmin}/>
</div>
</Router>
Please help me solve this problem. Thank you
This is because you have the component at root level, so it'd render in all pages.
Try including it as child to ChooseRole, DashboardGeek and DashboardAdmin components individually. That should solve the problem.
There are a few ways around this, it depends on the scale of your application but IMO the simplest is below.
I would assume you are using a boolean or a user object in your App or global state? Try adding a boolean to render the Nav like: {this.state.loggedIn ? <Nav /> : ''}
You can also generate an 'AppTemplate' component that passes the routes as props.children or includes it in every page.
I have the following JSON object...
{ name: "Jessie" }
And I want to be able to pass it through my Router so that it can be displayed on my pages. For example, this is my root page...
StaticPage.jsx
export default class StaticPage extends React.Component {
render() {
return (
<div>
<Router history={hashHistory}>
<Route path='/' component={Search} />
<Route path='/favorites' component={Favorites} />
</Router>
</div>
);
}
}
So passing this data to Search, I would imagine might look something like this...
<Route path='/' component={Search} name = {this.props.name}/>
However, nothing gets rendered when I do that. I have researched this quite a bit and understand, from what I've read, that you cannot pass objects through the Router. It's very odd bc Router looks like a traditional React component but does not function as such. None of the explanations of a work around seem clear to me. Could anyone provide me with an example using this code? I am using react-router 3.0. There didn't seem to be any magical solution with 4.0 so I figured I'd ask before upgrading. Thanks!
It's because the component prop of <Route> only renders the component with route props, not your supplied props.
You can use the render or component prop on a <Route> in React Router v4 to pass a function which returns a <Search> element that explicitly passes the name:
<Route path="/" render={() => <Search name={this.props.name} />} />
Or with component:
<Route path="/" component={() => <Search name={this.props.name} />} />
But you should prefer render or else you'll have lots of remounting. If you still plan to use route props, you can also do:
render={routeProps => <Search name={this.props.name} {...routeProps} />}
A whole different approach, one more elegant in my opinion is to use route params and pass the name directly through the URL:
<Route path="/:name" component={Search} />
When you navigate to /Bob, you can access this.props.match.params.name which'll give you "Bob".
It is not a good practice to pass the object data via the routes directly. It is recommended to pass a param such as name or id in this way:
<Route path='/favorites/:name' component={Favorites} />
And retrieve it from your request in the destination.
This is a duplicate issue: Pass object through Link in react router