Following the examples provided here https://reacttraining.com/react-router/examples/url-params.
I constructed my own route for my website like so:
<Router>
<div>
<Route path='/' exact component={Layout} />
<PublicRoute authed={this.state.authed} exact path='/login' component={Login} />
<PublicRoute authed={this.state.authed} exact path='/register' component={Register} />
<PrivateRoute authed={this.state.authed} path='/dashboard' component={Dashboard} />
<Route path='/cachaca/:bottle' component={Layout} />
<Route render={() => <h3>No Match</h3>} />
</div>
</Router>
That is pretty similar to the example given except for the fact that the navigation (or link to) is somewhere else in the page.
The route itself is working perfectly. I can get match information, i can sucessfully check whether a user is logged in or not and so forth.
My only problem is that the URL bar isn't changing to reflect the navigation changes, so if I am sitting at localhost:3000/cachaca/1234 and click to go home, the home page will load but the URL bar will still show 'localhost:3000/cachaca/1234'.
I've seen similar posts to mine from react router 2/3 that seem to indicate that the history prop is what's causing this issue - but I don't believe that to be the case in Router 4.
In addition to that, the example in the react training website works and mine (which seems to be following the same steps) does not.
Am I missing something here? Why am I not getting the same results as in the training website.
Here is the navigation link I am using:
<Link to={"/"}>Home</Link>
Appreciate any help,
Thanks!
Wow, this is silly - but I wasted my entire day in this so will post this in the hope of saving someone else some time:
Turns out it wasn't working because I was doing my testing on the webpack-dev-server/ folder and react-router 4 seems to have a problem with that.
As soon as I went from http://localhost:3000/webpack-dev-server/cachaca/asdf to http://localhost:3000/cachaca/asdf the website started behaving as it should - both in content and its location in the URL bar.
Happy coding to all!
Related
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.
what I want to do is that my website has admin panel with the following routes /admin/.... and then the normal web would be /...
But the urls come together when I enter / works well but when I enter /admin you see the web menu and the admin menu when you would only have to see the menu of the administrator I leave the demo that performed in CodeSandBox, I hope you can help me.
https://codesandbox.io/s/react-router-admin-users-5wzte
Greetings and thanks.
Wrap your routes in a Switch component. The Switch will return only the first matched route. More info is available here:
https://reacttraining.com/react-router/web/guides/basic-components
The issue is because, your path /admin getting matched with your both Route.
You need to provide an exact prop,
<Route
path="/"
exact //exact prop will help to match exact path
render={userProps => (
<Layout match={userProps.match}>
<h2>Menu Web</h2>
<Route exact path="/" component={Home} /> //Add exact here also otherwise it will get match for path `/contact`
<Route path="/contact" component={Contact} />
</Layout>
)}
/>
Also you need to make use of Switch, which
Renders the first child <Route> or <Redirect> that matches the location.
Just wrap your Route with Switch.
I'm trying to render a component that plays an mp3 on each route, and have the mp3 continue to play uninterrupted despite route changes. I'm not sure if this is possible, but i'd assume it is considering React creates single page sites.
So far i've only gotten as far as getting the component to render, my problem is all of the functionality along with the component has seized to work... I can't figure out why..
<HashRouter>
<Route path="/" render={props => <Music />} />
<Route exact path="/" component={Home} />
<Route path="/work" component={MyWork} />
<Route path="/about" component={About} />
</HashRouter>
The Route in question is the first one.
I've also tried this thinking I needed to pass down the props in order for the functionality to work
<Route path="/" render={props => <Music
changePlayingState={props.changePlayingState}
playing={props.state.playing}
showMusicMessage={props.showMusicMessage}
hideMusicMessage={props.hideMusicMessage}/>} />
A working version of the component(and how i would like it to appear on all routes) is shown on the home page(click play) code sandbox had a file size limit so i just added some popping sound effects(takes a second to start playing)
https://codesandbox.io/s/rlw1q45m1m
The only components regarding this question (and the only you'll need to view)are as follows:
Index.js
The routes: Home.js, About.js, MyWork.js
Music Component: Music.js
If any additional information is needed please ask! Thanks!
You just have to pull out the Music Component and make it a stand alone Component in itself then pass it down to the HashRouter within a single child and rest of the Routes.
Here is an example to implement NavBar in each route of the React App.
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'm trying to make a site that is fully protected behind a login page. Once the user has logged in, they are redirected to the main page, which has a completely different layout.
MainLayout (includes header, sidebar etc.)
LoginLayout (centers everything on the page, but doesn't include the above elements)
FutureLayout (I want to be able to use different layouts for different pages later)
At the moment, the biggest problem I'm facing is that every time I change my route, everything rerenders. I sort of understand why this is happening, but I've been unable to find a way around it.
Here is some of my current progress
First thought
https://codesandbox.io/s/6l10v4o7jn
const HomePage = () => (
<MainLayout>
<h1>HOME PAGE</h1>
</MainLayout>
);
This obviously doesn't work because every time you move to another page, the Pagererenders the Layout. You can see this in action by toggling the green button on either HomePage or AboutPage then switching routes
Second Thought
https://codesandbox.io/s/moj437no58
<Route render={() => (
<MainLayout>
<Route exact path="/" component={HomePage} />
<Route path="/about" component={AboutPage} />
</MainLayout>
)} />
<Route render={() => (
<LoginLayout>
<Route path="/login" component={LoginPage} />
</LoginLayout>
)} />
This fixes the state problem, but of course it renders both layouts because there's noting telling it not to, but the page is still determined by the router, as expected.
In a real application, those routes would be extracted to their own component like <AuthRoutes> and <PublicRoutes>
Why some solutions might not work
I'm also planning on using connectedRouterRedirect from redux-auth-wrapper to deal with protecting my routes, which can be a problem when using some of the suggestions I've found.
This I believe is because how the wrapper works as a HOC in conjuntion with Route's component prop
i.e <Route component={mustBeAuth(Page)} />
Conclusion
I've been stuck with this for a while, and I feel like I'm getting confused with various techniques and suggestions. Hopefully someone here can have a fresh take on it and perhaps help me figure out a solution.