I'm struggling to render a component based on routes from the React-Router. When the route is dynamic I can't seem to figure out a way to match the route.
I have created a component "Navbar" which I want to render only on specific routes. For this purpose I created a separate component "Tracker" which matches the route and renders the "Navbar" component.
Tracker.js snippet:
class Tracker extends Component {
render() {
var NavVisible =
this.props.location.pathname === "/feed" ||
this.props.location.pathname === "/explore" ||
this.props.location.pathname === "/chatroom" ||
this.props.location.pathname === "/username" ? (
<Navbar />
) : null;
return <div>{NavVisible}</div>;
}
}
Suppose if I have to display the Navbar component in "feed/(some_dynamic_path)", how should I write the condition?
A solution would be greatly appreciated.
Thanks.
Edit: App.js
import React, {Component} from 'react';
import {BrowserRouter, Switch, Route} from 'react-router-dom';
class App extends Component{
render(){
const DefaultContainer = () => (
<div>
<Route path='/home' component={ft}/>
<Route path='/explore' component={exp}/>
<Route path='/compose' component={add}/>
<Route path='/chatroom' component={msg}/>
<Route path='/user' component={usrpro}/>
</div>
)
return (
<BrowserRouter>
<div className="App">
<Tracker/>
<Switch>
<Route path='/' exact component={Splash}/>
<Route exact path="/(compose)" component={noNavContainer}/>
<Route exact path="/(newtext)" component={noNavContainer}/>
<Route path="/(login)" component={login}/>
<Route component={DefaultContainer}/>
</Switch>
</div>
</BrowserRouter>
);
}
}
export default App;
You can try something like:
this.props.location.pathname === '/feed/' + this.props.params.id
Might get you started: https://jaketrent.com/post/access-route-params-react-router-v4/
You can include Routers for various urls like this in your app.
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
class App extends React.Component {
render () {
return (
<Router>
<Home>
<Switch>
<Route path='/feed/:path_variable' component={Navbar} />
</Switch>
</Home>
</Router>
)
}
}
Also In Navbar Component, You will get access to this.props.match.params.path_variable whose value changes based on the URL.
Read more about it in this nicely written article.
https://scotch.io/courses/using-react-router-4/route-params
I am defining the routes in react
Global ones in the app-routes.js file and other in their respective components.
app.js
render() {
return (
<div className="App-wrap">
<AppRoutes/>
</div>
); }
app-route.js
export class AppRoutes extends React.Component {
render() {
return (
<Router>
<Switch>
<Route path="/" component={LayoutComponent} />
</Switch>
</Router>
);
}
}
layout-component.js
I have placed my header here for navigation
export class LayoutComponent extends React.Component {
render() {
return (
<LayoutWrap>
<HeaderComponent> </HeaderComponent>
<LayoutRoutes />
</LayoutWrap>
);
}
}
layout.routing.js
export class LayoutRoutes extends React.Component {
render() {
return (
<Switch>
<Route path="/" exact >
<Redirect to="/users"/>
</Route>
<Route path="/users" name="Users" component={UserComponent} />
<Route path="/permissions" name="Permissions" component={PermissionComponent} />
</Switch>
);
}
}
Now the issue is, when i am defining my child routes, my child routes are dependent on parent
i.e. i have to write parent's previous url in child.
If i change my app-routes.js path from "" to layout my routing will not work.
<Route path="/layout" component={LayoutComponent} />
How to solve the issue?
When you are defining your child Routes, you need to prefix the parent route path before it for these to work. For this you can make use of match.path from props like
export class LayoutRoutes extends React.Component {
render() {
const { match } = this.props;
return (
<Switch>
<Route path={match.path} exact render ={(props) => <Redirect to="/users"/>} />
<Route path=`${match.path}/users` name="Users" component={UserComponent} />
<Route path=`${match.path}/permissions` name="Permissions" component={PermissionComponent} />
</Switch>
);
}
}
Right now I am redirecting to a new page using but I dont know how to pass the data to the new page. This is what I am doing:-
class ShopsCatOptions extends React.Component{
constructor(props){
super(props);
this.state={
shopd: this.props.shop,
redirect:false
};
}
render() {
if (this.state.redirect) {
return <Redirect push to="./shopdetail" data={this.state.redirect}/>;
}
return(
<div class="expndinnerm" onClick={this.handleOnClick}>
{
this.state.shopd
}
</div>
)
}
}
export default ShopsCatOptions
What is the right way to do it?
What i have done to acheive this is by using history prop,
this.props.history.push({
pathname:"/shopdetail",
state:{
key:"value"
}
});
And in the ShopDetail component you can access the object like,
this.props.location.state.key
Edit: To get history in your props, your main component should have ,
import { BrowserRouter, Route } from 'react-router-dom';
...
<BrowserRouter>
<Switch>
<Route path="/home" component={Home} />
<Route path="/user" component={User} />
<Route path="*" component={page404} />
</Switch>
</BrowserRouter>
How to implement conditional routing i.e. if and only if some conditions satisfies, then routing should occur.
For example, if and only if the user enters the correct credentials, login should be successful and the user should be able to see the welcome page.
If we directly hit some URL like localhost:8080/welcome, that should not be navigated to welcome page. The welcome page should only be displayed after login.
How to achieve this, can anyone help me please?
App.js
import React, { Component } from 'react';
import Header from './Header';
class App extends Component{
render(){
return(
<div>
<Header />
</div>
);
}
}
export default App;
Header.js
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import Login from './Login';
import SignUp from './SignUp';
class Header extends Component{
render(){
return(
<div>
<nav class="navbar navbar-default">
<div class="container-fluid">
<ul class="nav navbar-nav">
<li><Link to={Login}>Login</Link></li>
<li><Link to={Login}>SignUp</Link></li>
</ul>
</div>
</nav>
</div>
);
}
}
export default Header;
AllRoutes.js
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import Login from './Login';
import SignUp from './SignUp';
import Welcome from './Welcome';
class AllRoutes extends Component{
render(){
return(
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={SignUp} />
<Route exact path="/Welcome" component={Welcome} />
</Switch>
);
}
}
export default AllRoutes;
Welcome.js
import React, { Component } from 'react';
class Welcome extends Component{
render(){
return(
<div>
<h2>Welcome to MainPage..</h2>
</div>
);
}
}
export default Welcome;
To help answer your question, I think you may need to also ask how that route should get blocked. Looking through the example above, you don't yet have a mechanism that helps answer the question of "should I be able to visit this page". That might come from state, redux, or some other means of determining if the user is logged in.
Since react-router is just plain React (one of my favorite parts!!) you have all the tools available to you that you would to conditionally show any part of your React app.
Here are a couple examples of how you might achieve this (by no means is this exhaustive. Be creative! It all depends on your requirements and the tools you are using)
class AllRoutes extends Component{
render(){
return(
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={SignUp} />
{ this.state.authenticated &&
<Route exact path="/Welcome" component={Welcome} />
}
</Switch>
);
}
}
One of my favorite ways to accomplish this is creating a ProtectedRoute component
class ProtectedRoute extends Component {
render() {
const { component: Component, ...props } = this.props
return (
<Route
{...props}
render={props => (
this.state.authenticated ?
<Component {...props} /> :
<Redirect to='/login' />
)}
/>
)
}
}
class AllRoutes extends Component {
render() {
return (
<Switch>
<Route path='/login' component={Login} />
<ProtectedRoute path='/welcome' component={Welcome} />
</Switch>
)
}
}
While I didn't include any specific logic to how state.authenticated was set, this may come from anywhere (by no means does it needs to come from state). Do your best to answer the question of "how do I determine whether a user is authenticated" and use that mechanism as the means to handle route authentication.
For that you need to break the entire app into two parts, normally accessible and protected part. Protected part will be accessible only after successful login.
To achieve that functionality, create a wrapper of protected part, and define its routes with path='/', and put the condition inside that. All the protected routes should be defined inside that wrapper component. If anyone try to access those routes without login, wrapper will redirect them to login page.
Like this:
class AllRoutes extends Component{
render(){
return(
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={SignUp} />
<Route path="/" component={AppWrapper} />
</Switch>
);
}
}
AppWrapper Component (assuming you are using some way to maintain whether user is logged-in or not, so put the proper check in if condition):
import { Redirect } from 'react-router-dom'
class AppWrapper extends Component{
render(){
if(/*not login*/)
return <Redirect to="/login" />
return(
<div>
App wrapper
<Route path='/Welcome' component={Welcome} />
</div>
);
}
}
I would like to join the party with simple solution.
Just conditional render in the component prop in as follows:
<Router>
<Navigation />
<Switch>
<Route
exact
path="/"
component={
loading
? () => <div>Loading posts...</div>
: () => <Home posts={posts} />
}
/>
<Route path="/login" component={Login} />
</Switch>
</Router>
Here i am trying to fetch some data from an api when it fetched (loading) should be false and renders Home component.
You can do something like:
let redirectToUrl;
if ( not logged in ) //check condition
{
redirectToUrl = <Redirect to={loginPage}/>;
}
and use the same:
<Router>
<div>
{redirectToUrl}
<Switch>
<Route />
</switch>
</div>
</Router>
For the same you need to import from react-router-dom:
import {
BrowserRouter as Router,
Route,
browserHistory,
Redirect,
Link,
Switch
} from "react-router-dom";
Best way is to create a HOC.
Considering you are maintaining auth state in your redux store. Or else you can check with your own variable.
Create requireAuth.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
export default function(ComposedComponent) {
class Authentication extends Component {
static contextTypes = {
router: React.PropTypes.object
}
componentWillMount() {
if (!this.props.authenticated) {
this.context.router.push('/');
}
}
componentWillUpdate(nextProps) {
if (!nextProps.authenticated) {
this.context.router.push('/');
}
}
render() {
return <ComposedComponent {...this.props} />
}
}
function mapStateToProps(state) {
return { authenticated: state.auth.authenticated };
}
return connect(mapStateToProps)(Authentication);
}
Now in the routes you can use this hoc and pass the component.
import RequireAuth from './requireAuth';
...
<Route exact path="/Welcome" component={RequireAuth(Welcome)} />
The best and simple thing you can do is to create a state variable login and route based on the boolean values. the logic to set is up to you. i can show an example of simple routing based on condition. I store my pages in a array and use the map function to switch to different routes. For an example I have inserted my DesignerHome.js for your reference
This is my App.js
import React,{Component} from 'react';
import{BrowserRouter as Router,Switch,Route,Redirect,} from 'react-router-dom'
import MainHome from './MainHome'
import DesignerHome from './designer/DesignerHome'
export default class App extends Component{
constructor(){
super()
this.state={
login : true,
r_page :[
{
path :'/designerhome',
component : DesignerHome,
},]
}
}
render(){
return(
<Router>
<Switch >
<Route path='/' exact component={MainHome}/>
{this.state.r_page.map((item , i)=>(this.state.login?
<Route exact {...item}/> : <Redirect to="/" /> ))}
</Switch>
</Router>
)
}
}
This is my DesignerHome.js
import React,{Component} from 'react';
export default class DesignerHome extends Component{
render(){
return(
<div>
designer home
</div>
)
}
}
create a state for authentication. based on that navigate to the page.
Also I used render instead of component in Route.
import React, { Fragment, useState, useEffect } from "react";
import Dashboard from "./components/Dashboard";
import Login from "./components/Login";
import Register from "./components/Register";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from "react-router-dom";
function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const setAuth = (boolean) => {
setIsAuthenticated(boolean);
};
useEffect(() => {
isAuth(); // to be implemented
}, []);
return (
<Fragment>
<Router>
<div className="container">
<NavigationCard />
<Switch>
<Route
exact
path="/login"
render={(props) =>
!isAuthenticated ? (
<Login {...props} setAuth={setAuth} />
) : (
<Redirect to="/dashboard" />
)
}
/>
<Route
exact
path="/register"
render={(props) =>
!isAuthenticated ? (
<Register {...props} setAuth={setAuth} />
) : (
<Redirect to="/login" />
)
}
/>
<Route
exact
path="/dashboard"
render={(props) =>
isAuthenticated ? (
<Dashboard {...props} setAuth={setAuth} />
) : (
<Redirect to="/login" />
)
}
/>
</Switch>
</div>
</Router>
</Fragment>
);
}
export default App;
I am rendering a Home component inside a Route so that I can pass state in as a prop to the component.
class App extends React.Component {
constructor(props){
super(props)
this.state = {
page: 'false'
};
}
render() {
return (
<Router>
<div>
<Switch>
<Route exact path='/' render={()=><Home page={this.state.page}/>} />
<Route exact path='/projects' component={Projects} />
<Route render={function(){
return <p>Not Found</p>
}} />
</Switch>
</div>
</Router>
)
}
}
Inside the Home component, I want to trigger a route change from a function. Because the Component is rendered inside the Route the history prop doesn't get passed in and therefore I cannot trigger a route change like so:
class Home extends React.Component{
constructor(props){
super(props);
this.gotoProjects = this.gotoProjects.bind(this);
}
gotoProjects() {
this.props.history.push('/projects');
}
render() {
return (
<button onClick={this.gotoProjects.bind(this)}>Projects</button>
)
}
}
How can I change routes from a component while still retaining it's props?
UPDATE
I've created a History.js using createBrowserHistory
import { createBrowserHistory } from 'history'
export default createBrowserHistory()
And updated App.js to be
import history from '../history';
class App extends React.Component {
constructor(props){
super(props)
this.state = {
page: 'false'
};
}
render() {
return (
<Router>
<div>
<Switch>
<Route exact path='/' render={()=><Home history={history} page={this.state.page}/>} />
<Route exact path='/projects' component={Projects} />
<Route render={function(){
return <p>Not Found</p>
}} />
</Switch>
</div>
</Router>
)
}
}
So now when I click the button in Home the url goes to /projects but the view is still rendering Home instead of Projects. How do I render Projects after the history.push happens?