I have created one simple project with 3 main components/pages.
Home page (path: "/") with is login page. once we click on login button, will create a dynamic URL "admin/random_num
LogIn.js
import { Link, Route } from "react-router-dom";
import Dashboard from "../Admin/Dashboard";
export default function Login(props) {
return (
<div>
<h1>Login form</h1>
<Link to={`/admin/${Math.random()}`}>Login Button</Link>
<Route path="/admin/:aid" component={Dashboard} />
</div>
);
}
2nd component/page is admin(path: "/admin/randum_num" , eg: "/admin/132"), which should be displayed when clicked on login button on login page.
Admin.js
export default function Dashboard(props) {
return (
<div>
<h1>Admin Dashboard</h1>
</div>
)
}
3rd component is 404page, which will simply display when no route is matched.
Error404.js
export default function Error404() {
return (
<div>
<h1>404</h1>
</div>
)
}
last not the least, in App.js i am setting up routing.
App.js
import React from 'react'
import './App.css';
import Error404 from './pages/Error404';
import Login from './pages/StartingScreen/Login';
function App() {
return (
<BrowserRouter>
<div className="App">
<Switch>
<Route exact path="/" component={Login} />
<Route component={Error404} />
</Switch>
</div>
</BrowserRouter>
);
}
export default App;
Note: i am able to display the admin dashboard page if i remove "exact" parameter in Route statement but after removing "exact" both Login and Dashboard page will be visible which i dont want. I want only one component should be visible at given route.
I am struggling to find an answer regarding this problem since one week, kindly help. If the given detail is incomplete, let me know.
Since you are using exact for your login component, that means your Login component will only render for the root path /.
And your Dashboard component is inside your Login component, that means you also need to add "/admin/:aid" to your login route.
You need to change this
<Route exact path="/" component={Login} />
to
<Route exact path={["/", "/admin/:aid"]} component={Login} />
Update:
To hide login button, you can use another Switch in your Login component
export default function Login(props) {
return (
<div>
<Switch>
<Route exact path="/">
<h1>Login form</h1>
<Link to={`/admin/${Math.random()}`}>Login Button</Link>
</Route>
<Route exact path="/admin/:aid" component={Dashboard} />
</Switch>
</div>
);
}
Related
I am using react router for a SPA Dashboard. I have these two routes(Routes.js) in dashboard right now. Also, a separate route in App.js for landing page.
Now it works fine as expected when it loads and when I click different routes. For example, When I click customers route(in sidebar), it open the customers section on the same page in right side. But, when I refresh the customers page localhost:5000/customers, now it opens as a separate page. How can I solve this.
I tried debugging it but still no luck.
Here's an example : https://github.com/gouravthakur39/MRE
Unfortunately can't reproduce it in code sandbox
Routes.js
import React from "react";
import { Route, Switch } from "react-router-dom";
import Dashboard from "../pages/Dashboard";
import Customers from "../pages/Customers";
const Routes = () => {
return (
<Switch>
<Route path="/home" exact component={Dashboard} />
<Route path="/customers" exact component={Customers} />
</Switch>
);
};
export default Routes;
Part of App.js
import { Fragment } from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import PrivateRoute from "./private/PrivateRoute";
import Landing from "./pages/Landing";
import Home from "./pages/Home";
import Customers from "./pages/Customers";
function App() {
return (
<Fragment>
<div className="App">
<BrowserRouter>
<Switch>
<Route exact path="/" component={Landing} />
<PrivateRoute exact path="/home" component={Home} />
<PrivateRoute exact path="/customers" component={Customers} />
</Switch>
</BrowserRouter>
</div>
</Fragment>
);
}
export default App;
Question/Issue
Based on your code and the description on the Dashboard component it sounds like you want Dashboard and Customers to render "nested" in the "/home" path.
const Dashboard = () => {
return (
<div>
<h1>Dashboard</h1>
<p>I want to open dashboard and customers here when clicked</p>
<p>
Right now, when you click customers, it will open it in new page and not
here.
</p>
</div>
);
};
This is because Home is rendering the Dashboard on the same "/home" path, and the sidebar is linking to a "/customers" path at the root router level. This Customers component is outside the nested home/dashboard component.
Solution
App
For this the root "/home" in App needs to not exactly match in order for it to render nested routes. Remember also that in the Switch component that path order and specificity matter. Order the paths from more specific to less specific, i.e. "/home" is more specific than "/" and should be ordered higher/before. The "/customers" route/path should be removed since it will be rendered by Home.
function App() {
return (
<Fragment>
<BrowserRouter>
<Switch>
<Route path="/home" component={Home} />
<Route path="/" component={Landing} />
</Switch>
</BrowserRouter>
</Fragment>
);
}
Routes (rendered by Home)
Use the useRouteMatch hook to gather the current matched path value and build the nested routes. Remember the path order and specificity rule for the Switch.
import { Route, Switch, useRouteMatch } from "react-router-dom";
const Routes = () => {
const { path } = useRouteMatch();
return (
<Switch>
<Route path={`${path}/customers`} component={Customers} /> // "/home/customers"
<Route path={path} component={Dashboard} /> // "/home"
</Switch>
);
};
Sidebar (rendered by Home)
Use the useRouteMatch hook to gather the current matched url value and build the nested links.
import { Link, useRouteMatch } from "react-router-dom";
const Sidebar = () => {
const { url } = useRouteMatch();
return (
<div>
<ul>
<li>
<Link to={url}>Dashboard</Link> // to "/home"
</li>
<li>
<Link to={`${url}/customers`}>Customers</Link> // to "/home/customers"
</li>
</ul>
</div>
);
};
Hey I am a Smart Contract Developer first time using react and when I try to switch pages using Router both pages Data are showing on a single page, I wanna ask is there some way to first load main page and when I click on another button the data of another page show.
App.js:
<Router>
<Link className="btn-success user-btn" to = "/user">User Login</Link>
<Link className="btn-success admin-btn">Admin Login</Link>
<Switch>
<Route path={"/user"} exact>
<User />
</Route>
</Switch>
</Router>
User.js:
import React from 'react';
const User = () =>{
return(
<div>
User Panel
</div>
);
}
export default User;
App Component is the main component in React which acts as a container for all other components. When you put some content in there, it will show up everywhere in your app. You can simply create another component, let's name it Home
Home.js
import React from 'react';
import { Link } from 'react-router-dom';
const Home = () =>{
return(
<div>
<Link className="btn-success user-btn" to = "/user">User Login</Link>
<Link className="btn-success admin-btn">Admin Login</Link>
</div>
);
}
export default Home;
App.js
import User from './User';
import Home from './Home';
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route exact path={"/"} component={Home}></Route>
<Route exact path={"/user"} component={User}></Route>
</Switch>
</Router>
</div>
);
}
export default App;
I have a router which has a link to a product details page, problem is, the application is being displayed in a android webview so ... if am viewing a product details page and clicked back, the whole application will exit.
is there a way to make the use go back to the home page ? or I have to implement a back button ?
class App extends React.Component {
render() {
return (
<div>
<Router>
<Switch>
{/* <PrivateRoute exact path="/" component={HomePage} /> */}
<Route exact path="/" component={Home} />
<Template>
<Route path="/:id" component={PropertyTemplate} />
</Template>
</Switch>
</Router>
</div>)
}
}
It's not neccessary to implement backbutton - just this code make work:
history.goBack()
Here is implemented BackButton - just add this in common folder in architecture & reuse.
import React from 'react';
import { withRouter } from 'react-router-dom';
import ReturnIcon from '../../img/tech/return-white-icon.svg'
const GoBack = ({ history }) => <img src={ReturnIcon} onClick={() => history.goBack()} alt="Go back" />;
export default withRouter(GoBack);
i am trying to use nested routing inside my application. in this case shop page is located on /shop url and i want to render CollectionsOverview on /shop/ and CollectionPage on /shop/:collection and the CollectionsOverview is working fine but
CollectionPage is not working. i even tried to use hardcoded path like /shop/abc but its still not working.. please help
version of my react-router-dom is ^5.0.0
import React from 'react';
import {Route, Switch,Router} from 'react-router-dom';
function CollectionPage() {
return (
<div className="App">
CollectionPage
</div>
);
}
function CollectionsOverview() {
return (
<div className="App">
CollectionsOverview
</div>
);
}
export default function ShopPage({match}){
return(
<div className='shop-page'>
<Router>
<Switch>
<Route exact path={`${match.path}`} component={CollectionsOverview}/>
<Route exact path={`${match.path}/:collectionId `} component={CollectionPage}/>
</Switch>
</Router>
</div>
)
}
Try this, since you already have a Router wrapping your App component, you don't need another one having it's own context in the ShopPage, since it's -supposedly- is originally routed from App, in the context of the Router that lives in the App
export default function ShopPage({match}){
return (
<div className='shop-page'>
<Switch>
<Route exact path={`${match.path}`} component={CollectionsOverview}/>
<Route exact path={`${match.path}/:collectionId `} component={CollectionPage}/>
</Switch>
</div>
);
}
You'll need the Switch only to determine navigating decision for it's Route components
I'm new to React. I have react router config in App.js like this:
<BrowserRouter>
<div className="App">
<Header />
<Switch>
<Route exact path="/" component={Home}>
</Route>
<Route exact path="/management" component={Management}>
</Route>
<Route exact path="/sign-up" component={SignUpForm}>
</Route>
<Route exact path="/sign-in" component={SignInForm}>
</Route>
<Route component={Error}>
</Route>
</Switch>
</div>
</BrowserRouter >
I want header to show in every page, there's a button of logout at header, I want to redirect to /sign-in page after I click it. In my header component it's like this:
class Header extends Component {
constructor(props) {
super(props);
this.state = {
redirect: false
}
}
logout = () => {
sessionStorage.setItem("userToken", '');
sessionStorage.clear();
this.setState({ redirect: true });
}
render() {
if (this.state.redirect) {
return (
<Redirect to={'/sign-in'} />
)
}
return (
<div>
<Navbar collapseOnSelect expand="md" bg="dark" variant="dark" fixed="top" >
......
<NavLink to="/management" className="header-link"><FontAwesomeIcon icon="cog" size="lg" /></NavLink>
<button type='button' onClick={this.logout}>Log Out</button>
</Nav>
</Navbar.Collapse>
</Navbar>
</div>
);
}
}
export default Header;
There will be errors "Warning: You tried to redirect to the same route you're currently on: "/sign-in", and the nav bar will disappear only the body of sign-in shows. May I know what is the correct way to do this? I also tried this.props.history.push('/sign-in') but there's no props.history, probably because header is not in route? Should i use with Router? Or should I actually just make every page import header instead put it in app.js? or what is actually the right way to do this? Thank you so much for your help!
You can implement login/logout with route using HOC that checks the session item with every route change. If the session has userToken then it will redirect to given component otherwise will redirect to login component.
import React from "react"
import {Redirect} from "react-router-dom"
export const PrivateRoute = ({component: Component, ...rest}) => (
<Route {...rest} render={(props) => (
sessionStorage.getItem('userToken') ? <Component {...props} /> : <Redirect to="/sign-in"/>
)} />
)
import <PrivateRoute> and use it as the authorized path. And keep all the other path as normal routes in which you don't want authorization.
<BrowserRouter>
<div className="App">
<Header />
<Switch>
<PrivateRoute path="/" component={Home} />
<PrivateRoute path="/management" component={Management} />
<Route path="/sign-up" component={SignUpForm} />
<Route path="/sign-in" component={SignInForm} />
<Route component={Error} />
</Switch>
</div>
</BrowserRouter >
So while you do log out, the session item will be removed and automatically redirect to sign-in page.
class Header extends Component {
....
logout = () => {
sessionStorage.removeItem("userToken");
sessionStorage.clear();
}
render() {
return (
<div>
...
<button type='button' onClick={this.logout}>Log Out</button>
</div>
)
}
}
export default Header;
Components tied up to Routes gets access to history object as prop so you can mutate it as you need, such as logging out. Since your Header component doesn't have access to the history object, you will have to use a lower level router to give it access to the history object:
import { Router } from "react-router"
import { createBrowserHistory } from "history"
const history = createBrowserHistory()
<Router history={history}>
<div className="App">
<Header history={history} />
<Switch>
<Route exact path="/" component={Home}>
</Route>
<Route exact path="/management" component={Management}>
</Route>
<Route exact path="/sign-up" component={SignUpForm}>
</Route>
<Route exact path="/sign-in" component={SignInForm}>
</Route>
<Route component={Error}>
</Route>
</Switch>
</div>
</Router>
now inside you Header component you can call history.push('/login')
see reference: https://reacttraining.com/react-router/web/api/Router/history-object
There are two approaches you're mentioning here. You can use the higher order component 'withRouter' that gives components access to the history object. By using the history object that would get passed to your component as a prop, you can push to the route you want.
Personally, I like setting up my signout links to render a component that houses the log-out logic and renders a redirect to log-in once it's complete. That way users can go directly to the sign-out link if they want, and you can link to it from anywhere in your app as needed, without having to duplicate the logic.
In your browser router, you can add a path for "/logout" that renders a component like this (based on your logic):
import React, { Component } from 'react';
import { Redirect } from 'react-router';
export default class LogOut extends Component {
state = {
redirect: false,
};
componentDidMount() {
sessionStorage.setItem("userToken", '');
sessionStorage.clear();
this.setState({ redirect: true });
}
render() {
return this.state.redirect ?
<Redirect to={'/sign-in'} /> :
null;
}
}
Normally I would make an ajax request to clear a session and then setState once that's complete, but yours is all server-side.