ReactJS Navigate on a specific route (Router v4) - javascript

So I'm kinda new to ReactJS I would like to achieve the following thing with my code -
const App = () => (
<div>
<Header />
<Route path="/:page" exact component={PageWrapper} />
<Route path="/" exact>
<Redirect to="/home" />
</Route>
</div>
);
So I would like to achieve that if user enters /home, /about, /anythingElse as the link it does not navigate, but if user loads a page without passing page parameter, for example - localhost:3000/ or localhost:3000, then it automatically navigates user to localhost:3000/home .
Currently it also navigates to home if user enters a page. I thought exact meant that exact route has to match for it to load.
Is it possible?

You shouldn’t nest routes in V4.
You can use the Switch component from the router to ensure only one route is matched.
This way all routes that don’t match the /:page route will redirect:
const App = () => (
<div>
<Header />
<Switch>
<Route path="/:page" exact component={PageWrapper} />
<Redirect to="/home" />
</Switch>
</div>
)
Remember to import the Switch component.

Related

Navigating between multiple params - React js

So I've been working on a project lately using React js (I thought it would be similar to React native), while I pretty much understand most of it as I previously worked with React native a lot. There are still some new things I'm learning for example the react-router-dom npm. So I understand the basics and how it works, but I'm trying to use parameters which change depending on the user (User ID).
The code below shows how I'm currently using my router. While going to home (/) and /user/:id works, I can't go to /user/:id/settings. If I try going to the settings it renders both the /user/:id page and below it renders settings page.
What I want to be able to do is if the user is in the user/:id page they can click a button which takes them to the user/:id/settings instead of the current issue where it renders the setting page below the user page.
App.jsx
export class App extends React.Component {
render() {
return (
<Router>
<Route exact path="/" component={Home} />
<Route path="/user/:id" component={User} />
<Route path="/user/:id/settings" component={Setting} />
</Router>
)
}
};
User.jsx
render() {
return (
<div>
{/* Cool information about the user */}
<div
className="optionContent"
onClick={() => {
let uri = `/user/${this.props.match.params.id}/settings`;
this.props.history.push(uri)
}}
>
Press me
</div>
</div>
);
}
Extra information:
I have tried using variable parameters for users but I wasn't able to full make those work as once the user enters /user/:id page the buttons update the url but not the parameters in this.
I need to have the ID within the url to fetch from the API and some other stuff
Variable url: /user/:id/:type?
This is because with React Router v5 which is currently the latest version as v6 is completed, the routes aren't exact by default which means that for each of the routes, if the current route starts with the route of a component, this component will be displayed.
For your example:
<Router>
<Route exact path="/" component={Home} />
<Route path="/user/:id" component={User} />
<Route path="/user/:id/settings" component={Setting} />
</Router>
If the current route is "/user/user1" then it only matches the User component.
If the current route is "/user/user1/settings/ then it matches User and Settings components so they will both be rendered as you are finding.
To fix it, simply use the exact keyword on the component with the fewer requirements.
<Router>
<Route exact path="/" component={Home} />
<Route exact path="/user/:id" component={User} />
<Route path="/user/:id/settings" component={Setting} />
</Router>

React Router - 404 page always showing due to custom route component

I have a component which takes in similar props to the standard react-router Route and returns a route. This returned route has a render prop which just passes through the component but adds a nav component. This custom NavRoute does not work with the react-router Switch component, as when I use the custom component inside the switch along with a catch-all 404 page, the page always shows.
How can I make the 404 component only show if none of the other routes match the url?
NavRoute
const NavRoute = ({ exact, path, component: Component }) => {
return (
<Route exact={exact} path={path} render={(props) => (
<div style={styleSideNav}>
<Nav />
<Component {...props} />
</div>
)}/>
);
});
Usage
<Switch>
<NavRoute exact path="/" component={Home} />
<NavRoute exact path="/about" component={About} />
<Route path="/" component={Page404} />
</Switch>
EDIT:
Codesandbox of full example: https://codesandbox.io/s/react-router-stack-overflow-5jcum
Url of codesandobx: https://5jcum.csb.app/invite/abc
Switch only works with the first level of components directly under it. It can't traverse the entire tree.
https://github.com/ReactTraining/react-router/issues/5785#issuecomment-351067856
To fix that, we can pass array of routes instead of routes wrapped in fragment. I have made the changes and updated in codesandbox.
https://codesandbox.io/s/react-router-stack-overflow-hu62w?file=/src/App.js

React Router: Routes within routes

I am building a React app that has a static marketing site and a dynamic app. I am using a combination of React Router and hooks to separate the two and ensure proper routing throughout.
To begin with, I want users who are logged in to be taken directly to the app when they hit the root ("/") and to the static/marketing site when not logged in. The main marketing site home page has a nav bar that allows users to access other routes such as "/about", whereas the app has a separate nav bar for app navigation.
The problem is, while the authentication based routing for the root route seems to be working, and I can navigate to other routes specified in my top-level file, the routes that are included within my static/marketing site are not accessible.
Top-level/index.js
const routing = (
<Provider store={store}>
<Router>
<NavWrapper />
{/* <Switch> */}
<Route exact path="/" component={AuthWrapper} />
<Route path="/signup" component={SignUp} />
<Route path="/login" component={Login} />
{/* </Switch> */}
</Router>
</Provider>
)
ReactDOM.render(routing, document.getElementById('root'));
AuthWrapper.js (here useAuth() is a React hook that evaluates global Redux isAuthenticated state)
const AuthWrapper = (props) => {
return useAuth() ? <App /> : <Website />
}
export default AuthWrapper;
website/index.js
export const Website = () => {
return (
<>
{/* <SiteNav /> */}
<Switch>
<Route exact path="/" component={LandingPage} />
<Route path="/about" component={AboutPage} />
<Route path="/how-it-works" component={HowItWorksPage} />
<Route path="/plans" component={PlansPage} />
<Route path="/press" component={PressPage} />
<Route path="/faq" component={FAQPage} />
<Route path="/legal" component={LegalPage} />
<Route path="/for-dieticians" component={DietitiansPage} />
<Route path="/for-trainers" component={PTsPage} />
</Switch>
</>
)
};
In my app, when I enter localhost:3000/ (unauthenticated) I am correctly taken to my home/landing page. However, if I try to use the navigation (or type in any subroutes) to access "/about" for example, I get a blank screen (other than the nav bar). In the React dev tools, the component isn't even rendering.
I think this is the issue:
<Route exact path="/" component={AuthWrapper} />
You want Authwrapper to be shown for about as well, right? But the above only matches "/" exactly.
You should enable the switch statement, but move the AuthWrapper Route to the bottom and then let it have this path: "/:rest*" (maybe "/*" works too, haven't used react-router in a while).
That way, it will use the Route if the first 2 don't match.

What's the proper way to redirect with React routing when a user is not logged into the application?

In a React SPA, I have a collection of "pages" under the /src/pages/ folder.
The entry point page is an index.js file under the /src/ folder, where I define a router const like this:
const routing = (
<Router>
<div>
<Switch>
<Route path="/signIn" component={SignIn} />
<Route exact path="/" component={Homepage} />
<Route path="/page1" component={Page1} />
<Route path="/page2" component={Page2} />
<Route path="/page3" component={Page3} />
<Route component={NotFound} />
</Switch>
</div>
</Router>
It works great and all. All pages are navigable like "https://mysuperapp.com/page2" and it will render the Page2 React component.
Concerns arise when I incorporate user session management (log in, log out). If a user is not logged in the app, all pages should automatically redirect to the /signIn page. And viceversa, if a user is already logged, if the /signIn page is accessed, it should automatically redirect to the root homepage.
Right now I have implemented this by adding the following code to all the pages, right after the render() method is declared in the component, like this:
class Page2 extends React.Component {
render() {
if (UserProfile.getUserSessionStatus() !== "logged") {
this.props.history.push("/signIn");
}
}
return (
JSX code to be rendered here...
...
This works, but it feels like a cheap workaround used by someone who is just learning React, not by a professional.
For a proof of concept it works, but I would never dare to use such a thing in a production environment.
So, what's the right, best-practices-aligned way to accomplish this?
One possible approach is to create a Higher Order component(HOC) and use it for protecting any routes that require login.
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
UserProfile.getUserSessionStatus() === "logged"
? <Component {...props} />
: <Redirect to='/login' />
)} />
)
And then use like this
.....
<PrivateRoute path='/page1' component={Page1} />
.......
Hope this helps!

React-router.Nested routes

How to make nested routes???
I want initial route to be course/:course_id? after that, when i click on a node I want my url to become course/:course_id?/nodes/:node_id .
I use : "react-router-dom": "^4.2.2"
return (
<Router>
<div id="app-main">
<Header />
<Route path="/course/:course_id?" component = {Content}/>
<Route path="/course/:course_id?/nodes" component = {Content}/>
<Footer />
</div>
</Router>
);
When i click id redirects me to course/nodes and skips :course_id
return(
<div className="paragraph-text-child" onClick={() => this.props.select(chapter)} key={chapter.node_id} >
<Link to="nodes">{chapter.text}</Link>
{this.iterate(chapter.nodes)}
</div>
);
I think you have some concepts mixed up... Route is for handling received URLs, Link is for setting it.
Link does not know about Route, Route does not know about Link. Link sets the URL to what is specified in to. So if your current URL is /course, and to="nodes", the result is /course/nodes. If it was to="0/nodes", the result would be /courses/0/nodes.
Now if I understood correctly, you always want a number between "/courses" and "/nodes", correct?
This can be achieved with Redirect, which comes from react-router-dom too.
If you create the following Route:
<Route path="/courses" render={()=> <Redirect to="/courses/0"/>}/>
And rework the previous route so that course_id is NOT optional
<Route path="/course/:course_id" component={Content}/>
When you navigate to /courses, you will be silently redirected to courses/0. The result is that your Link component with to="nodes" will always redirect to courses/number/nodes - because effectively the courses/ location won't be reachable anymore. Every URL that would not contain a course_id, will be redirected to course_id = 0
Note that these 2 routes should be put in a Switch, and in the correct order, otherwise you will end up redirecting every time...
I have not tested this, but it should do the job:
...
<Header/>
<Switch>
<Route path="/course/:course_id" component={Content}/>
<Route path="/courses" render={()=> <Redirect to="/courses/0"/>}/>
</Switch>
<Footer/>
...
And this should handle /course/course_id
Now, if you want to nest a /course/:course_id/nodes/:node_id Route, that should go into the component rendered by the parent route.
Let's rework our parent Route into this:
<Route path="/course/:course_id" render={(props) => <Content ...props />}/>
What this does is, instead of just rendering the passed component, it renders the component and passed down Router props. Which means that the rendered component will be able to handle routes!
Now, in the Content component:
render() {
return <Route path="/course/:course_id?/nodes/:node_id?" component={NodeContent}/>
}
The last thing we need to do is change the to property of the Link component so that it will redirect to the target node:
<Link to={"nodes/" + chapter.node_id}/>
Does this make sense? I may have missed some gotchas in your code - the idea of what you want to achieve is in here, but you may have to adapt it a bit...

Categories