So I have this app which have the same layout in all routes except in two, Login and Register. I have been trying to have a totally separate layout for Login and Register routes but I'm unable to do so.
The App component have all the routes and I'm checking with conditionals the value of of the components which I want to hide with the help of window.location.pathnam. If the user is in '/' (which is the login route) or '/register', then return empty string. If they are in some other routes, then return those components.
But right now the login/register route doesn't show the components but if I go to an inner route where there were supposed to be header, sidebar, footer components, I don't see them. They disappeared from the routes where they are supposed to be.
What am I doing wrong here?
Here's the App comp:
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Header from './components/Header/Header';
import Sidebar from './components/Sidebar/Sidebar';
import Footer from './components/Footer/Footer';
import style from './App.module.css';
import RegisterScreen from './screens/RegisterScreen/RegisterScreen';
import LoginScreen from './screens/LoginScreen/LoginScreen';
import Screen3 from './screens/Screen3/Screen3';
import Screen4 from './screens/Screen4/Screen4';
import Screen5 from './screens/Screen5/Screen5';
import Screen6 from './screens/Screen6/Screen6';
const App = () => {
let header = window.location.pathname === '/' || '/register' ? '' : <Header />;
let sidebar = window.location.pathname === '/' || '/register' ? '' : <Sidebar />;
let footer = window.location.pathname === '/' || '/register' ? '' : <Footer />;
return (
<Router>
{/* <Header /> */}
{header}
<div className={style.appBody}>
{/* <Sidebar /> */}
{sidebar}
<main className={style.main}>
<Switch>
<Route path='/' component={LoginScreen} exact />
<Route path='/register' component={RegisterScreen} exact />
<Route path='/screen3' component={Screen3} exact />
<Route path='/screen4' component={Screen4} exact />
<Route path='/screen5' component={Screen5} exact />
<Route path='/screen6' component={Screen6} exact />
</Switch>
</main>
</div>
{/* <Footer /> */}
{footer}
</Router>
)
}
export default App;
Use useLocation from react-router-dom to get the current pathname.
With the .split() function you're able to get the last item.
In the components that change depending on the location (Header, Footer, ...), create your condition (using ternary operator or switch case condition).
Examples :
Header.js (with ternary operator)
import React from 'react';
import { useLocation } from 'react-router-dom';
export default function Header() {
const path = useLocation().pathname;
const location = path.split('/')[1];
return (
<div>
{location === 'login' ? (
<div>
</div>
) : (
<div>
</div>
)}
</div>
);
}
Footer.js (with switch case condition)
import React from 'react';
import { useLocation } from 'react-router-dom';
import ComponentA from './components/ComponentA';
import ComponentB from './components/ComponentB';
import ComponentC from './components/ComponentC';
export default function Footer() {
const path = useLocation().pathname;
const location = path.split('/')[1];
const checkLocation = (uri) => {
switch (uri) {
case 'login':
return <ComponentA />;
case 'register':
return <ComponentB/>;
case 'about':
return <ComponentC />;
default:
return null;
}
};
return (
<div>
{checkLocation(location)}
</div>
);
}
Demo: Stackblitz
Create two different layout , my suggestion is create src/layouts folder and save here your layouts. On your app.js or index.js use react router two switch between different layouts
Ps. Basically your sidebar, footer, navbar should be inside of your customize layout not on app.js or index.js
Related
I have been receiving white blank pages when trying to create multiple pages within my app and I have been using the router-dom to try and fix this but still can't understand why. Here is my code with Home and Navigation js being inside a components folder in the src directory and App.js just inside the src directory.
App.js
import React, { Component } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Home from './components/Home';
import Glazing from './components/Glazing';
import Navigation from './components/Navigation';
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Navigation />
<Route path="/" component={Home} exact/>
<Route path="/glazing" component={Glazing}/>
</div>
</BrowserRouter>
);
}
}
export default App;
Nav.js
import React from 'react';
import { NavLink } from 'react-router-dom';
const Navigation = () => {
return (
<div>
<NavLink to="/">Home</NavLink>
<NavLink to="/glazing">Glazing</NavLink>
</div>
);
}
export default Navigation;
Home.js
import React from "react";
import logo from '../logo.svg';
import './Home.css';
import "#fontsource/dm-sans";
function home() {
return (
<div className="Home">
<header className="Home-header">
<h1>EPC RATING PREDICTOR</h1>
</header>
<button> GET STARTED</button>
</div>
);
}
export default Home;
If you are using react-router-dom#6 then there are a couple things you need to address.
The Switch component was replaced by the Routes component and all Route components must be wrapped/rendered directly by Routes, or another Route component in the case of nesting routes.
The Route component API changed; gone are the component, and render and children function props, all replaced by a single element prop taking a ReactNode, a.k.a. JSX, value.
Example:
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './components/Home';
import Glazing from './components/Glazing';
import Navigation from './components/Navigation';
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/glazing" element={<Glazing />} />
</Routes>
</div>
</BrowserRouter>
);
}
}
See the Upgrading from v5 guide for other changes.
Upon entering my credentials, I successfully can redirect to the <Gallery/> component. However, once on the <Gallery/> component, if I want to switch from /gallery to /, I shouldn't be able to unless I logout (that of which I haven't implemented, yet) but unfortunately - I'm able to and I'm not sure why.
My question is: How's this happening and how can I remain on /gallery even if I manually go to my browser's search bar and switch my path to / which's the <Login/> component? I think something might be off with my <ProtectedRoute/> but I'm unsure.
Here's App.js:
import ReactDOM from 'react-dom';
import LoginSignUp from "./LoginSignUp/LoginSignUp";
import Gallery from "./Gallery/Gallery";
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import ProtectedRoute from "./ProtectedRoute/ProtectedRoute";
function App() {
return (
<>
<Switch>
<Route exact path="/" component={LoginSignUp} />
<ProtectedRoute component={Gallery} path='/gallery'/>
</Switch>
</>
);
}
if (document.getElementById("main")) {
ReactDOM.render(<Router><App/></Router>, document.getElementById("main"));
}
Here's ProtectedRoute.js:
import React from "react";
import {Redirect, Route, useHistory} from "react-router-dom";
function ProtectedRoute({ component: Component, ...restOfProps }) {
const isAuthenticated = localStorage.getItem("token");
console.log("this", isAuthenticated);
return (
<Route
{...restOfProps}
render={(props) =>
isAuthenticated ? <Component {...props} /> : <Redirect to='/' />
}
/>
);
}
export default ProtectedRoute;
Here's a Link component that I made:
const Restaurants = () => {
const meatList = require('./Meat-Shops.json');
return meatList.map((restaurant) => {
return (
<Link to={`/menu/${restaurant.store_name}`}>
<div className="restaurant-container">
<div className="image-container">
<img src={restaurant.image} alt={restaurant.alt} />
</div>
<div className="info-container" >
<h4>{restaurant.store_name}</h4>
<h5>Pick-up</h5>
</div>
</div>
</Link>
);
})
}
When pressed, it will take me (or render) this component:
import React, { useState } from 'react';
import { useParams } from 'react-router-dom'
const Menu = () => {
console.log(useParams());
return (
<div>
<p>Hello world</p>
</div>
)
}
export default Menu
What I am trying to do is to access restaurant.store_name (the URL parameter, variable at the end of the template string of the 'to' prop of the Link component) within the Menu component by using useParams(). But when I invoke useParams(), I get the following error:
error_message_invalid_hook. What I am doing wrong? Also, here is the file that contains the Router tags, if needed:
import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Menu from './Menu';
import Restaurants from './Restaurants';
import NavBar from "./NavBar"
const Pages = () => {
return (
<Router>
<NavBar />
<Switch>
<Route exact path="/">
<Restaurants />
</Route>
<Route path="/menu/:store_name" children={<Menu />} />
</Switch>
</Router>
)
}
export default Pages;
And here is the error message when I set:
const { menu } = useParams();
within the body of the 'menu' component (error message: error_message_for_useParams).
You can also check out all of my code for the components (the Menu and Restaurants components) here on my GitHub: https://github.com/Toriong/quick_eat_carnivore_search_and_delivery
You're close to the solution here. In this situation useParams() is looking at the :store_name, so you would destructure store_name from the params: const { store_name } = useParams() inside of your functional component.
I am a newbie to react and currently developing an application,
BACKGROUND
It has admin, faculty, student, dashboards.amd a static landing page with buttons to go to /admin/login ,/faculty/login ,/student/login. which opens respective dashboards [![Once admin logins he gets this dashboard page ][1]][1]
PROBLEM:
App.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import Routes from "./routes";
import firebase from "./Firebase";
const App = props => {
return (
<BrowserRouter>
<Routes {...props} />
</BrowserRouter>
);
};
firebase.auth().onAuthStateChanged(user => {
ReactDOM.render(<App user={user} />, document.getElementById("root"));
});
Routes.js
import React from "react";
import { Switch, Route, BrowserRouter } from "react-router-dom";
import MainLayout from "./OtherComponents/Common/MainLayout";
//Home
import Home from "./MainComponents/Home";
import HomePublicRoute from "./OtherComponents/Routes/Home/HomePublicRoute";
//ADMIN
//components
import AdminLogin from "./OtherComponents/Login/Admin";
import AdminDash from "./MainComponents/Admin";
import AdminPublicRoute from "./OtherComponents/Routes/Admin/AdminPublicRoutes";
import AdminPrivateRoute fro"./OtherComponents/Routes/Admin/AdminPrivateRoutes";
//pages
import PageDashboard from "./MainComponents/Admin/pages/dashboard";
import AdminTaluka from "./MainComponents/Admin/pages/taluka";
const Routes = props => {
console.log(props);
return (
<MainLayout>
<Switch>
<AdminPublicRoute
{...props}
exact
restricted={true}
path="/admin/login"
component={AdminLogin}
/>
<AdminPrivateRoute
{...props}
path="/admin/admindashboard"
exact
component={AdminDash}
/>
<AdminPrivateRoute
{...props}
path="/admin/pagedashboard"
exact
component={PageDashboard}
/>
<AdminPrivateRoute
{...props}
path="/admin/taluka"
exact
component={AdminTaluka}
/>
<HomePublicRoute path="/" component={Home} />
</Switch>
</MainLayout>
);
};
export default Routes;
MainLayout.js
import React from "react";
//var reactRouterToArray = require("react-router-to-array");
const MainLayout = props => {
//console.log(reactRouterToArray(props.children));
return <div>{props.children}</div>;
};
export default MainLayout;
Sidebar doesn't open the content inside the main container instead it opens in a new tab if i use route inside routes.js .
Sidebar doesn't open the content if i use route inside AdminDash.js .
i have tried passing the routes using props (props.children) to AdminDash( its not receivng the prop)
I am using private and public routes.
I am confused and I don't know where am I going wrong any suggetions , or hints would be helpful.
Thanks in advance.
Sidebar doesn't open the content inside the main container instead it opens in a new tab if i use route inside routes.js .
Inside LeftDrawer you are rendering links to the content. You should use react-router-dom Link elements.
Like this:
import { Link } from 'react-router-dom'
<Link to="/about">About</Link>
Sidebar doesn't open the content if i use route inside AdminDash.js .
Routes shouldn't be defined multiple times. You have defined /admin/pagedashboard both in AdminDash.js and Routes.js. If you want to have a shared shell around the content, define routes inside the shell component and remove exact route from the parent routes.
Example:
MainLayout
.Routes
../admin
...AdminDash (Shell for multiple pages, admin layout)
..../admindashboard
.....AdminDasboard
..../pagedashboard
.....PageDashboard
i have tried passing the routes using props (props.children) to AdminDash( its not receivng the prop)
You should just import them when needed.
EDIT:
Remove exact from the Routes.js <Route path='/admin' component={AdminDash} /> and in AdminDash try
const AdminDash = ({ match }) => (
<div>
<Route path={${match.url}/admindashboard} component={AdminContent}/>
<Route path={${match.url}/pagedashboard} component={PageContent}/>
</div>
);
In the above example take note to change AdminContent and PageContent with the names of your components.
I have this index.js:
<Provider store={store}>
<Router history={history}>
<App/>
</Router>
</Provider>
this App.js:
<Switch>
<Route exact path="/" component={Home} />
<Route
path="/login"
render={() => <Login userError={this.state.userError} />}
/>
<Route path="/registration" component={Registration} />;
</Switch>
and Home.js:
<div className="Home">
<Header/>
<div className="content">
<Sidenav/>
<Switch>
<Route path="/friends" component={Friends}/>
</Switch>
<Feed/>
</div>
</div>
I want Friends component to be rendered inside content block, but now if I try to reach /friends route via Link I am getting blank page. If I set /friends Route in App.js, it will be OK, but I won't have it in my content class, because it will be another page.
May you give me a hand with that?
Also in feature I will be have more items to display in content, that's why I put Switch in Home.js
Thanks in advance!
Move your content class and <Friends>
The issue you're having is that the component Home is not rendering when you visit /friends because it will only render when you go to /
To fix this just move the Route into the App.js file, along with the content class into the Friends component.
To make this easier, you could make your content class into a component. This way you could wrap it around all of the stuff you render.
Or move <Friends> and wrap content
What I mean by this is that you could also create your own Route component that wraps whatever component passed to it in a Content component. It might look similar to this:
const ContentRoute = ({ component, ...props }) => (
<Route {...props} component={() => (
<Content>
<component />
</Content>
)}>
</Route>
)
You can access demo here
Here what I have done. This demonstrates how to set layout when page's changing.
- src/
-- components/
--- Header.js
--- Sidenav.js
-- pages/
--- Home.js
--- Login.js
--- withBase.js
-- App.js
-- BaseLayout.js
-- routes.js
At first, let's make dummy components.
components/Header
import React from 'react';
export default () => (
<div>
This is Header.
</div>
);
components/Sidenav
import React from 'react';
export default () => (
<div>
This is Sidenav.
</div>
);
Then, pages.
pages/Home
import React from 'react';
import { NavLink } from 'react-router-dom';
import withBase from './withBase';
const Home = () => (
<div>
<p>Welcome Home!!!</p>
<NavLink to="/login">Go to login page</NavLink>
</div>
);
export default withBase(Home);
pages/Login
import React from 'react';
import { NavLink } from 'react-router-dom';
import withBase from './withBase';
const Login = () => (
<div>
<p>You have to login here...</p>
<NavLink to="/">Go home</NavLink>
</div>
);
export default withBase(Login);
pages/withBase
import React from 'react';
export default WrappedComponent => (
class extends React.Component {
componentDidMount() {
this.props.showHeaderSidenav();
}
render() {
return <WrappedComponent />;
}
}
);
As you see, withBase is a HOC. It runs showHeaderSidenav when the page is mounted.
App
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import BaseLayout from './BaseLayout';
import routes from './routes';
export default class extends React.Component {
state = {
withHeaderSidenav: true
}
showHeaderSidenav = (withHeaderSidenav = true) => {
this.setState({ withHeaderSidenav });
}
render() {
return (
<BaseLayout withHeaderSidenav={this.state.withHeaderSidenav}>
<Switch>
{routes.map(route => (
<Route
exact
key={route.path}
path={route.path}
render={() => (
<route.component
showHeaderSidenav={() => this.showHeaderSidenav(route.withHeaderSidenav)}
/>
)}
/>
))}
</Switch>
</BaseLayout>
);
}
}
BaseLayout
import React from 'react';
import Header from './components/Header';
import Sidenav from './components/Sidenav';
export default ({ withHeaderSidenav, children }) => (
<div>
{withHeaderSidenav && <Header />}
<div className="content">
{withHeaderSidenav && <Sidenav />}
{children}
</div>
</div>
);
We can say that BaseLayout is like a wrapper. It contains dynamic components which will be shown based on withHeaderSidenav prop.
Finally...
routes
import Home from './pages/Home';
import Login from './pages/Login';
export default [
{
path: '/',
component: Home,
withHeaderSidenav: true
},
{
path: '/login',
component: Login,
withHeaderSidenav: false
},
];
You could have moved(declared) content component inside Friends component. I do not see the reason why content component should live outside of Friends component. You can declare content component inside any component that needs it.Content component does not have to mess with routing implementation