Seperate route from rest of layout - react - reactjs

I have an app that I want to add an admin page to. I want the admin page to have its own layout seperate from the client layout. With what I have what's the current and 'best' way to implement this?
app.js
import './App.css';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import HomePage from './pages/HomePage';
import ItemDetailPage from './pages/ItemDetailPage';
import Header from './components/Header';
import Footer from './components/Footer';
import CollectionPage from './pages/CollectionPage';
import AdminPage from './pages/AdminPage';
function App() {
return (
<Router>
{/* Admin app routes */}
{/* <Route exact path="/admin" component={AdminPage}/> */}
{/* Client app routes */}
<div className="app">
<Header />
<Switch>
<Route exact path="/" component={HomePage}/>
<Route exact path="/item/:itemID" component={ItemDetailPage}/>
<Route exact path="/collections/:collection" component={CollectionPage}/>
</Switch>
<Footer />
</div>
</Router>
);
}
export default App;
HOC
export default function ClientLayoutHOC(props) {
const {component: Component, ...rest} = props;
return (
<div className="app">
<Header />
{/*<Component {...rest}/> */}
{props.children}
<Footer />
</div>
)
}
I found this. Should I create an AdminLayout and ClientLayout components and filter the pages through?

You could create a High Order Component and add it to your non-admin pages like this:
The HOC can contain your div wrapper and the Header and Footer.
Then all of your routes stay clean in the Router.Switch
The anonymous functions for HOC, HomePage, ItemDetailPage, and CollectionPage below are meant to be samples of the changes you'll make to those components. The HOC component will be a separate component too.
const HOC = (props) => {
const {component: Component, ...rest} = props;
return (
<div className="app">
<Header/>
<Component {...rest}/>
<Footer/>
</div>
)
}
const HomePage = (props) => {
return (
<HOC>
{/* replace with HomePage content*/}
</HOC>
)
}
const ItemDetailPage = (props) => {
return (
<HOC>
{/* replace with ItemDetailPage content*/}
</HOC>
)
}
const CollectionPage = (props) => {
return (
<HOC>
{/* replace with CollectionPage content*/}
</HOC>
)
}
function App() {
return (
<Router>
<Switch>
{/* Admin app routes */}
<Route exact path="/admin" component={AdminPage}/>
{/* Client app routes */}
<Route exact path="/" component={HomePage} />
<Route exact path="/item/:itemID" component={ItemDetailPage}/>
<Route exact path="/collections/:collection" component={CollectionPage}/>
</Switch>
</Router>
);
}
In looking at your added HOC code I would suggest the following changes:
export default function ClientLayoutHOC(props) {
const {component: Component, children, ...rest} = props;
return (
<div className="app">
<Header />
<Component {...rest}>
{children}
</Component>
<Footer />
</div>
)
}

Related

TS2559: Type '{ children: Element[]; }' has no properties in common with type 'IntrinsicAttributes'

I want to use react-router-dom by React with TypeScript. I have a typescript error in <Router> at Home.jsx.
Error
Home.tsx
Type '{ children: Element[]; }' has no properties in common with type 'IntrinsicAttributes'.TS2559(9)
App.tsx
import React from 'react'
import Home from './pages/Home'
function App() {
return (
<div>
<Home />
</div>
)
}
export default App
Home.tsx
import React from 'react'
import { Button, TitleDesc } from '../components/Index'
import { Link } from 'react-router-dom'
import Router from '../router'
const Home: React.FC = () => {
return (
<>
<Router>
<div>
<div>Hii</div>
<div>
<Link to='/login'><Button color='green'>Login</Button></Link>
<Link to='/register'><Button color='blue'>Register</Button></Link>
</div>
</div>
<TitleDesc title='Hi' desc='Hi' />
</Router>
</>
)
}
export default Home
router.tsx
import * as React from 'react'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
import Login from './pages/Login'
import Register from './pages/Register'
import Home from './pages/Home'
const Router = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/Login' component={Login} />
<Route exact path='/Register' component={Registerl} />
<Route component={() => <h1>204 No Content</h1>} />
</Switch>
</BrowserRouter>
)
}
export default Router
It's because of you don't call {props.children} in the router.tsx. Change it to bellow code will remove the error:
const Router = : React.FunctionComponent (props) => {
return (
<BrowserRouter>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/Login' component={Login} />
<Route exact path='/Register' component={Registerl} />
<Route component={() => <h1>204 No Content</h1>} />
{props.children}
</Switch>
</BrowserRouter>
)
}
But logically it doesn't need to do something as you did. Because your Router will handle the routes and you don't need to use it again in the Home. So instead of using Home in App, transform it to use Router in your App. Thus your code can change to this:
App.tsx
function App() {
return (
<div>
<Router/>
</div>
)
}
export default App
Home.tsx
const Home: React.FC = () => {
return (
<>
<div>
<div>Hii</div>
<div>
<Link to='/login'><Button color='green'>Login</Button></Link>
<Link to='/register'><Button color='blue'>Register</Button></Link>
</div>
</div>
<TitleDesc title='Hi' desc='Hi' />
</>
)
}
export default Home
Router.tsx
This component won't change.
I think Router only accepts a single child. You have two: div and TitleDesc. Wrap those in a <> and it should be fine:
const Home: React.FC = () => {
return (
<>
<Router>
<> {/* <-- new wrapper */ }
<div>
<div>Hii</div>
<div>
<Link to='/login'><Button color='green'>Login</Button></Link>
<Link to='/register'><Button color='blue'>Register</Button></Link>
</div>
</div>
<TitleDesc title='Hi' desc='Hi' />
</> {/* <-- new wrapper */}
</Router>
</>
)
}

useParams() for none route components in ReactJS

Please go through my App.js
<Provider store={reduxStore}>
<Router basename={process.env.PUBLIC_URL}>
<Container>
<Navigation /> <----------------------------------- HERE
<div className="wrapper">
<Switch>
<PublicRoute exact path={PATHS.HOME} component={Home} />
</Switch>
</div>
<Footer/> <---------------------------------------- HERE
</Container>
</Router>
</Provider>
Navigation is use in all components so I added that component in App.js. For sure, this component has no route linked to it. BUT:
In Navigation and Footer, I want to use useParams().
Navigation.js
import React from 'react';
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import { withRouter, useParams } from 'react-router-dom';
const Navigation = (props) => {
const { slug } = props;
return (
<>
<Navbar>
<Nav>
<Nav.Link>
Home
</Nav.Link>
</Nav>
</Navbar>
</>
);
};
export default withRouter(Navigation);
My current url:
localhost:3000/event/:slug
I do recieve props, but I could not find slug
try remove exact ?
<Switch>
<PublicRoute path={PATHS.HOME} component={Home} />
</Switch>

React Router - How to hide components on 404 page

I am building an application using react and using react router to manage the routing. I am currently handling the 404 error. My problem is that my application is displaying my navbar and subfooter and footer when at the 404 page i created is displayed. whats the best way to hide these 3 components?
my app.js currently looks like this..
class App extends Component {
render() {
return (
<div className="body">
<div className="App">
<BrowserRouter>
<NavBar />
<Switch>
<Route exact path="/" component={Home} exact={true} />
<Route exact path="/projects" component={Projects} />
<Route component={PageNotFound}/>
<Redirect to="/404" />
</Switch>
<SubFooter/>
<Footer />
</BrowserRouter>
</div>
</div>
);
}
}
you need to create a main route which contain a route for main app (contain nav + contents routes +footer) and a route for 404 page:
const Main = () => (
<BrowserRouter>
<Switch>
<Route path="/404" exact render={() => <div>404</div>} />
<Route path="/" component={App} />
</Switch>
</BrowserRouter>
);
for app js :
import React from "react";
import { Redirect, Route, Switch } from "react-router-dom";
import "./styles.css";
export default function App() {
return (
<div className="App">
<div>navbar</div>
<div>footer</div>
<Switch>
<Route path="/" exact render={() => <div>home</div>} />
<Route exact path="/projects" render={() => <div>projects</div>} />
<Redirect to="/404" />
</Switch>
</div>
);
full example here :example
note : you need to place 404 route before main app route to match first.
You can make a layout for error something like:
layout/Error404.js
import React, { Suspense } from 'react';
export const ErrorLayout = (props) => {
const { children } = props;
return (
<div>
<div>
<Suspense fallback={"loading..."}>
{children}
</Suspense>
</div>
</div>
);
}
layout/publicLayout.js
import React, { Suspense } from 'react';
export const PublicLayout = (props) => {
const { children } = props;
return (
<div>
<NavBar />
<div>
<Suspense fallback={"loading..."}>
{children}
</Suspense>
</div>
<SubFooter/>
<Footer />
</div>
);
}
and create a custom route to render what you need for example:
routes/myErrorRoute.js
import React from "react";
export const ErrorRouteLayout = (props) => {
const { layout: Layout, component: Component, ...rest } = props;
return (
<Route
{...rest}
render={(matchProps) =>
Layout? (
<Layout>
<Component {...matchProps} />
</Layout>
) : (
<Component {...matchProps} />
)
}
/>
);
};
routes/publicRoute.js
import React from "react";
export const PublicRouteLayout = (props) => {
const { layout: Layout, component: Component, ...rest } = props;
return (
<Route
{...rest}
render={(matchProps) =>
Layout? (
<Layout>
<Component {...matchProps} />
</Layout>
) : (
<Component {...matchProps} />
)
}
/>
);
};
and finally, your app.js should look something like this
import { ErrorLayout } from './layout/Error404'
import { PublicLayout } from './layout/publicLayout'
import { ErrorRouteLayout } from './routes/myErrorRoute'
import { PublicRouteLayout } from './routes/publicRoute'
class App extends Component {
render() {
return (
<div className="body">
<div className="App">
<BrowserRouter>
<PublicRouteLayout
component={YourComponent}
exact
path="/home"
layout={PublicLayout}
/>
<ErrorRouteLayout
component={YourComponent}
exact
path="/error404"
layout={ErrorLayout}
/>
<Redirect to="/404" />
</Switch>
</BrowserRouter>
</div>
</div>
);
}
}

How to implement nested Routing (child routes) in react router v4?

The component tree i want is as below
- Login
- Home
- Contact
- About
Contact and About are children of Home.
This is my App.js ,
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Route exact path="/home" component={HomeView} />
</div>
</BrowserRouter>
);
}
}
render(<App />, document.getElementById('root'));
This is Home,
export const HomeView = ({match}) => {
return(
<div>
<NavBar />
Here i want to render the contact component, (Navbar need to stay)
</div>
)
}
This is my Navbar,
export const NavBar = () => {
return (
<div>
<Link to="/home">Home</Link>
<Link to="/home/contact">Contact</Link>
<hr/>
</div>
)
}
Contact component just need to render "hello text".
To make nested routes you need to remove exact:
<Route path="/home" component={HomeRouter} />
And add some routes:
export const HomeRouter = ({match}) => {
return(
<div>
<NavBar />
{/* match.path should be equal to '/home' */}
<Switch>
<Route exact path={match.path} component={HomeView} />
<Route exact path={match.path + '/contact'} component={HomeContact} />
<Switch>
</div>
)
}
You don't need use match.path in nested routes but this way it will be easier to move everything from "/home" to "/new/home" in case you decide to change your routing.

React-Router nested routes loading blank page instead of loading inside parent component

I am new to React and trying to create a layout with nested routes. Here's my scenario
show Login when URL is /
show Dashboard when URL is /dashboard
show Profile when URL is /dashboard/profile (this should load
inside the dashboard content area)
The login page and dashboard page are loading properly when the URL is accessed in the browser but for /dashboard/profile, the browser goes to a blank page instead of loading it inside the dashboard component.
Index.js
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root'));
App.js
class App extends Component {
render() {
return (
<div>
{/* <Switch> */}
<Route exact path='/' component={SignIn}/>
<Route exact path='/dashboard' component={Dashboard}/>
{/* </Switch> */}
</div>
);
}
}
export default App;
Dashboard.js
class Dashboard extends React.Component {
render() {
const { classes } = this.props;
return (
<React.Fragment>
<CssBaseline />
<div className={classes.root}>
<Header classes={classes} open={this.state.open} click={this.handleDrawerOpen} />
<Sidebar classes={classes} open={this.state.open} click={this.handleDrawerClose} />
<main className={classes.content}>
<div className={classes.appBarSpacer} />
*********I expect profile component to load here
but when I access the URL /dashboard/profile I get a new blank page*********
Route path="/dashboard/profile" exact component={Profile} />
</main>
</div>
</React.Fragment>
);
}
}
You need to remove the exact prop from the Dashboard route (present in Switch) while doing the child routing.
This is the minimal implementation of your use case:
import React, { Component } from "react";
import "./styles.css";
import {
NavLink,
Redirect,
Route,
BrowserRouter as Router,
Switch
} from "react-router-dom";
const App = () => (
<Router>
<div className="App">
<ul>
<li>
<NavLink to="/login">Login</NavLink>
</li>
<li>
<NavLink to="/dashboard">Dashboard</NavLink>
</li>
</ul>
<Switch>
<Route exact path="/login" component={Login} />
<Route path="/dashboard" component={Dashboard} />
</Switch>
</div>
</Router>
);
const Login = () => <span>Login Page</span>;
const Dashboard = () => {
return (
<div>
<div>Dashboard Page</div>
<NavLink to="/dashboard/profile">Go to profile</NavLink>
<div>
<Route exact path="/dashboard/profile" component={Profile} />
</div>
</div>
);
};
const Profile = () => {
return <span>Profile Page</span>;
};
export default App;
You can find the working example here:https://codesandbox.io/s/z3py3672v3

Resources