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>
Related
I am facing little bit difficulty, I have to manage my dashboard using material-UI with other components
the process of application is that first I want to open login foam and then the dashboard and I am willing to change just the right-center part of the dashboard with the different component by selecting the listItem option
here is my Dashboard
here is all routes defined on my App.js
<Provider store={store}>
<BrowserRouter>
< Routes>
<Route exact path='/' element={<Login/>} />
<Route exact path='/dashboard' element={<Dashboard/>} />
<Route exact path='/product' element={<Product/>}/>
</Routes>
</BrowserRouter>
</Provider>
currently, the issue is that when I am going to login which is on my "/" route and after the authentication the app navigate on the dashboard but when I click my product tab so the dashboard is disappear there
here is my product page
i am willing to that all pages should to render just in text area where text is showing
I will make some assumptions before answering your question:
You want the Dashboard view to be always available
The Dashboard view's white screen should receive different content depending on the route that you're currently in
In your app you should have some sort of Layout which wraps the entire App. You are using react-router v6 so most probably the way to go it will be this:
<Provider store={store}>
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route exact path='/login' element={<Login/>} />
<Route exact path='/dashboard' element={<Dashboard/>} />
<Route exact path='/product' element={<Product/>}/>
</Route>
</Routes>
</BrowserRouter>
</Provider>
And in your Layout component, something like this:
import { Outlet } from 'react-router-dom';
const Layout = () => (
<main>
<Outlet />
</main>
);
You can check the react-router documentation for <Outlet />
Did it help?
EDIT: Forgot to say that yout <Layout /> view should have the Drawer that you're showing in your first screenshot, and the white space is where Outlet should be.
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
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!
I am trying to have separate routes but same component for add/edit forms in my react app like the below:
<Switch>
<Route exact path="/dashboard" component={Dashboard}></Route>
<Route exact path="/clients" component={Clients}></Route>
<Route exact path="/add-client" component={manageClient}></Route>
<Route exact path="/edit-client" component={manageClient}></Route>
<Route component={ NotFound } />
</Switch>
Now in the manageClient component, I parse the query params (I pass in a query string with client id in edit route), I render conditionally based on the query param passed.
The problem is that this doesn't remount the whole component again. Say an edit page is opened, and the user clicks on add component, the URL changes, but the component doesn't reload and hence remains on the edit page.
Is there a way to handle this?
Using different key for each route should force components to rebuild:
<Route
key="add-client"
exact path="/add-client"
component={manageClient}
/>
<Route
key="edit-client"
exact path="/edit-client"
component={manageClient}
/>
One solution is use inline function with component, that will render a new component each time, But this is not a good idea.
Like this:
<Route exact path="/add-client" component={props => <ManageClient {...props} />}></Route>
<Route exact path="/edit-client" component={props => <ManageClient {...props} />}></Route>
Better solution would be, use componentWillReceiveProps lifecycle method in ManageClient component. Idea is whenever we render same component for two routes and switching between them, then react will not unmount-mount component, it will basically update the component only. So if you are making any api call or require some data do all in this method on route change.
To check, use this code and see it will get called on route change.
componentWillReceiveProps(nextProps){
console.log('route chnaged')
}
Note: Put the condition and make the api call only when route changes.
<Route exact path={["/add-client", "/edit-client"]}>
<manageClient />
</Route>
Reference
Version 5.2.0
https://reacttraining.com/react-router/web/api/Route/path-string-string
My problem was we used an common path in-between, which causes dynamic path to not working
<Switch>
<Route key="Home" path="/home" component={Home} />
<Route key="PolicyPlan-create" path="/PolicyPlan/create" component={PolicyPlanCreatePage} />
{/* <Route key="PolicyPlan-list" path="/PolicyPlan" component={PolicyPlanListPage} /> */}
<Route key="PolicyPlan-list" path="/PolicyPlan/list" component={PolicyPlanListPage} />
<Route key="PolicyPlan-edit" path="/PolicyPlan/edit/:id" component={PolicyPlanCreatePage} />
<Route key="cardDesign" path="/cardDesign" component={cardDesign} />
<Route key="Admin-create" path="/admin/create" component={RegisterPage} />
</Switch>
So don't use the path like the commented one, now the code is working
.................
this.props.history.push("/PolicyPlan/edit/" + row.PolicyPlanId);
.............
You can simply provide an array of paths in a single route tag as follows -
<Route exact path={["/add-client", "/edit-client"]} component={manageClient}></Route>
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.