Use layout for certain routes in React - reactjs

I need to maintain a section of my app available for all pages (most of the pages actually)
to do that I wrapped the routes in a Layout
<Router>
<Route path="/login" exact strict component={Login}/>
<Layout>
<Route path="/list" exact strict component={List}/>
<Route path="/settings" exact strict component={Settings}/>
</Layout>
</Router>
the Layout component looks like this
class Layout extends Component {
render() {
return (
<React.Fragment>
<div>this box should be visible in all pages using layout</div>
<div>{this.props.children}</div>
</React.Fragment>
)
}
}
this work perfectly fine. the only poblem is when I go to /login
is rendering the Login component and also the Layout component.
I shouldn´t render the Layout if is not for one of the ¨protected¨ routes.
is there a way to achieve that without moving the layout inside of the List of Settings component?
the reason for this is because the box that´s fixed in all pages makes one API request and there´s no need to do that same API call for /list and /settings. I do it only once.
hope this makes sense to you. thank you!
update:
"react-router-dom": "^5.0.1"

I've done a fair bit of research on the subject and came up with this. I have it set up in my routes:
import React from 'react'
import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-dom'
// import PublicRoute, PrivateRoute, Login, Private
<Router>
<Switch>
<PublicRoute exact path='/login' component={Login} />
<PrivateRoute path='/private' component={Private} />
</Switch>
<Router>
Then in my privateRoute.js:
import React from 'react'
import { Redirect, Route } from 'react-router-dom'
// import useAuth and PrivateLayout
export default ({ componet: Component, ...rest }) => {
const { token } = useAuth() // wrapper I wrote to handle auth
if (!token) return <Redirect to='/login' />
return (
<Route {...rest} render={props => (
<PrivateLayout><Component {...props} /></PrivateLayout>
)} />
)
}
And my publicRoute.js is similar but without authentication. My layouts are a simple wrapper, all you need to do is include children:
export default ({ children }) => {
return (
<>
<Topbar />
<Sidebar />
{children}
</>
)
Note: For those who don't know <> is the shorthand for <React.Fragment>.

Related

How to defend routes in reactJS

I came across a problem which I dont know how to fix. I dont even know where to start.
The problem is next : I have url "localhost:4000/" If I will type url "localhost:4000/homepage" It will redirect me to my home page,But i want to restrict this access if token is not preserved.
Here are my routes :
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import LogIn from "./components/LogIn/Login";
import Register from "./components/Register/Register";
import Homepage from './components/HomePage/HomePage'
import Profile from './components/Profile/Profile'
import Posts from './components/addPostUI/Posts'
import "./app.css";
function App() {
return (
<Router>
<div className="App">
<Switch>
<Route path={"/"} exact component={LogIn} />
<Route path={"/login"} component={LogIn} />
<Route path={"/registration"} component={Register} />
<Route path={'/homepage'} component={Homepage} />
<Route path={'/profile'} component={Profile} />
<Route path={"/posts"} component={Posts} />
</Switch>
</div>
</Router>
);
}
export default App;
Any suggestions on how to restrict access and defend my routes?
There are many ways to restrict your Routes conditionally. The basic approach you can follow here is:
<Router>
<Switch>
<Route
path='/homepage'
render = {() => {
if(accessToken || localStorage.getItem('accessToken')) {
return <Homepage />;
} else {
return <Redirect to='/login' />;
}
}}
/>
</Switch>
</Router>
You can follow along with these steps whenever there is a need for conditional routing.
First determine what you want to authenticate users by, then you can do the following inside the page you want to protect, before the page renders.
if (failed access condition) return <Redirect to='/some route you want to redirect to' />
Be sure to import Redirect like this:
import { Redirect } from 'react-router-dom'
Also Consider
If you have a database, be sure to restrict access to the data based on the access token as well and don't depend on client side code to protect data.

How to make the drawer appear only on private routes?

I am developing a system with a login, and I want the drawer to appear only on routes that are private. How can I do that, below is a code that I already tried and was not successful.
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Home from "./Home";
import Login from "./Login";
import Details from "./Details";
import { AuthProvider } from "./Auth";
import PrivateRoute from "./PrivateRoute";
import Drawer from "./components/Drawer";
const App = () => {
return (
<AuthProvider>
<Router>
<Route exact path="/login" component={Login} />
</Router>
<Router>
<RoutePrivate />
</Router>
</AuthProvider>
);
};
const RoutePrivate = () => {
return (
<Router>
<Drawer />
<PrivateRoute exact path="/" component={Home} />
<PrivateRoute exact path="/:id" component={Details} />
</Router>
);
};
export default App;
You could do conditional rendering of drawer
const RoutePrivate = () => {
return (
<Router>
{window.location.pathname !== '/login' ? <Drawer /> : null}
<PrivateRoute exact path="/" component={Home} />
<PrivateRoute exact path="/:id" component={Details} />
</Router>
);
};
Good luck
You can go with few options.
you can remove <Drawer /> from router and render it inside <PrivateRoute /> along with matched router component (Home or Details etc).
Inside Drawer component, you can check auth and render null if auth failed. This way public routes will never get to see Drawer compoennt.
create a common layout and put the router inside layout. Check this. Also I got a random codesandbox (not fully working) but you can see the code reference there.

React router show only route without components defined in my browser

Hello in my directions file I set my struct
header
navbar
and my switch
foote
const AppRouter = () => (
<BrowserRouter>
<Route path="/login" component={AuthPage} exact={true} />
<Route path="/dashboard/addProduct" component={AddProduct} exact={true} />
<div>
<Header/>
<Navigation/>
<Container maxWidth="lg" >
<Switch>
<Route path="/" component={LandingPage} exact={true} />
<Route path="/xd" component={AuthPage} exact={true} />
<Route component={NotFoundPage} />
</Switch>
</Container>
</div>
</BrowserRouter>
);
But I have two routes where I didn't want to show my header
footer
and nav bar
which are the login and addproduct routes
how could i do that?
This is a little bit hacky (just to match your current approach) since I'm assuming you just want to hide those components in only those 2 routes, You can use withRouter in your Header and Navigation components, withRouter will provide to you the location prop and you can have a condition like this:
import React from "react";
import { withRouter } from "react-router-dom";
const Navigation = props => {
const { location } = props;
const { pathname } = location;
if (pathname !== "/login" || pathname !== "/dashboard/addProduct" ) {
return (
// Component logic goes here
);
}
// if we're on those routes we should return null to not render anything
return null;
};
export default withRouter(Navigation);
If you want a more robust long term approach this answer could help:
How to hide navbar in login page in react router

Prevent Navigation Component Rendering on Single Route but Render on every other Route

So I have a single Route /reminder, where I do not want my Navigation component, but I want the Navigation component to render on every other Route and I'm not sure how to go about this?
This is my App.js where I am using react-router-dom for my routing.
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Navigation from "./components/navigation/Navigation";
import Reminder from "./components/reminder/Reminder ";
const App = () => {
return (
<>
<Switch>
<Route path="/reminder" component={Reminder} exact />
</Switch>
<Navigation />
<MainContent>
<Router>
<Route path={"/"} component={Dashboard} exact />
</Router>
</MainContent>
</>
);
}
I had a look around for similar issues, but it's mostly for authenticated pages, this isn't for an authenticated page.
I don't want to go down the route of having to add the Navigation component into each of my 21 current routes, it seems like there should a better method?
With my current code above, it renders the Reminder component but still renders the Navigation component.
You can access the match data using a custom hook from react-router. All you need to do is
import { useRouteMatch } from "react-router-dom";
function App() {
let match = useRouteMatch("/reminder");
// Do whatever you want with the match...
return (
<>
<Switch>
<Route path="/reminder" component={Reminder} exact />
</Switch>
{!match && <Navigation />} <---Conditional rendering
<MainContent>
<Router>
<Route path={"/"} component={Dashboard} exact />
</Router>
</MainContent>
</>
);
}
Also, while I didn't change this in your example, it's not generally a good idea to have a route outside of the router component. I'm actually a little surprised react-router-dom didn't throw an error for you.

How to call a method from a component using Route in React Router v4?

I have some nested routes written in react router v4 and in the Navigation component I have an input box.
On submit I need to call a method on a different route (on Landing component) and pass the value. I can't find any example to call a method in Route.
Any other way/ workaround to use a navigation with data and different routes is welcome.
My routes:
return (
<div>
<Navigation callSearch = {this.callSearch.bind(this)} />
<Switch>
<Route path="/u/:slug" component={AuthorPage}/>
<Route path="/:location/:slug" component={PhotoDetails}/>
<Route path="/" component={Landing} />
</Switch>
</div>
)
In Navigation i call callSearch() :
searchPhotos(e) {
e.preventDefault();
if(this.state.searchTerm) {
this.props.callSearch(this.state.searchTerm);
}
}
I have fixed this issue in my application using withRouter from react-router module.
import { Route } from "react-router-dom";
import { withRouter } from "react-router";
class SideBar extends Component {
callSearch = (searchKeyword) => {
if(searchKeyword) this.props.history.push(`/u/${searchKeyword}`);
};
render() {
return (
<div>
<Navigation callSearch = {this.callSearch} />
<Switch>
<Route path="/u/:slug" component={AuthorPage}/>
<Route path="/:location/:slug" component={PhotoDetails}/>
<Route path="/" component={Landing} />
</Switch>
</div>
)
}
export default withRouter(SideBar);
Hope this will help anyone else !!!

Resources