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>
);
}
}
Related
The problem is really simple. I fetched the data and stored it in state. When passing to other components it logs okay, on a second render I get what I want(array of objects). But when I pass the same array to Fahrzeugdaten component, I get undefined on a second render.
PS. No need to write about different ways of state management, I need a simple answer.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import {
Datenschutz,
Fahrzeugangebote,
Fahrzeugankauf,
Finanzierung,
Galerie,
Home,
Impressum,
Kontakt,
Fehler404,
Fahrzeugdaten,
} from './pages';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import './App.css';
const App = () => {
const [carsList, setCarsList] = useState();
useEffect(() => {
fetchCars();
}, []);
const fetchCars = async () => {
try {
const res = await axios.get(url);
const data = res.data;
setCarsList(data);
} catch (error) {
console.log(error.response.data.error);
}
};
return (
<Router>
<Routes>
<Route path='/' element={<Home />} />
<Route path='/datenschutz' element={<Datenschutz />} />
<Route
path='/fahrzeugangebote'
element={<Fahrzeugangebote />}
/>
<Route path='/fahrzeugankauf' element={<Fahrzeugankauf />} />
<Route path='/finanzierung' element={<Finanzierung />} />
<Route path='/galerie' element={<Galerie />} />
<Route path='/impressum' element={<Impressum />} />
<Route path='/kontakt' element={<Kontakt />} />
<Route path='*' element={<Fehler404 />} />
<Route path='/fahrzeugdaten/:id'
element={<Fahrzeugdaten carsList={carsList} />}/>
</Routes>
</Router>
);
};
export default App;
Fahrzeugdaten component:
import React from 'react';
import { Header, Navbar, Jumbotrone, Logo, CarPage } from '../components';
const Fahrzeugdaten = ({ carsList }) => {
return (
<>
<div id='header-img-id' className='header-img-div'>
<Header />
<Navbar />
<div className='mka__logo-img'>
<div className='mka__logo-size'>
<Logo />
</div>
</div>
<div className='mka__title-wrapper'>
<div className='mka__container'>
<h1 className='mka__title'>FAHRZEUGDATEN</h1>
</div>
</div>
</div>
<div>
<CarPage />
</div>
<Jumbotrone />
</>
);
};
export default Fahrzeugdaten;
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>
</>
)
}
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>
)
}
I am using react router v4 and I'm trying to wrap my head around a react-router / redux / HOC related issue. I have a higher order component working. The HOC itself is connect()-ed to redux store. This approach works perfectly if I wire it up in a <Route /> via a component prop: <Route path="/profile" component={ withAuth(Profile) } /> does work.
However, when I try to do the same with a <Route /> and a render prop it does not work: <Route path="/profile" render={ () => withAuth(Profile) } /> The console throws "Route.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object." It does work when I omit the HOC: <Route path="/profile" render={ () => <Profile /> } /> so I suspect a problem with the HOC but I can't find it.
The reason I'm trying to use render is I'd like to pass additional props to the HOC. Besides it bugs me that I can't find the bug.
Can anybody with a fresh eye have a look and put me on the right path? Thanks!
/* === app.js === */
import React, { Component } from 'react';
import { Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import Header from './header';
import Home from './home';
import Content from './about';
import Profile from './profile';
import withAuth from './withAuth';
import store from '../reducers/store';
export default class App extends Component {
render() {
return (
<Provider store={store}>
<div className="mdl-grid">
<Header />
<main className="mdl-layout__content">
<div className="page-content">
<Route path="/" exact component={Home} />
<Route path="/about" component={Content} />
<Route path="/profile" render={ () => withAuth(Profile) } />
</div>
</main>
</div>
</Provider>
)
}
}
/* === withAuth.js (Higher order component) === */
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Redirect } from 'react-router-dom';
const HOC = WrappedComponent => {
return class extends Component {
render() {
if (this.props.auth) {
return <WrappedComponent authenticated={this.props.auth} {...this.props} />
} else {
return <Redirect to="/" />
}
}
}
}
function mapStateToProps({ auth }) {
return { auth };
}
export default WrappedComponent => connect(mapStateToProps)( HOC(WrappedComponent) );
The reason it doesn't work is because, here
<Route path="/profile" render={ () => withAuth(Profile) } />
render is actually assigned a function withAuth and not the returned value. What you need to do is
const AuthProfile = withAuth(Profile);
export default class App extends Component {
render() {
return (
<Provider store={store}>
<div className="mdl-grid">
<Header />
<main className="mdl-layout__content">
<div className="page-content">
<Route path="/" exact component={Home} />
<Route path="/about" component={Content} />
<Route path="/profile" render={ (props) => <AuthProfile {...props}/> } />
</div>
</main>
</div>
</Provider>
)
}
}
The difference between
render={ () => withAuth(Profile) }
and
render={ (props) => <AuthProfile {...props}/> }
is that in the first case its an arrow function that is bound to the context. Whereas in the second case its a function returning a component
I think your problem is with the way you use <Redirect />, you have to put it in <Route />. Look at this example:
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
fakeAuth.isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)
)}/>
)
I'm pulling my hair out trying to render multiple layouts with React Router v4.
For instance, I'd like pages with the following paths to have layout 1:
exact path="/"
path="/blog"
path="/about"
path="/projects"
and the following paths to have layout 2:
path="/blog/:id
path="/project/:id
Effectively what's being answered here but for v4: Using multiple layouts for react-router components
None of the other answers worked so I came up with the following solution. I used the render prop instead of the traditional component prop at the highest level.
It uses the layoutPicker function to determine the layout based on the path. If that path isn't assigned to a layout then it returns a "bad route" message.
import SimpleLayout from './layouts/simple-layout';
import FullLayout from './layouts/full-layout';
var layoutAssignments = {
'/': FullLayout,
'/pricing': FullLayout,
'/signup': SimpleLayout,
'/login': SimpleLayout
}
var layoutPicker = function(props){
var Layout = layoutAssignments[props.location.pathname];
return Layout ? <Layout/> : <pre>bad route</pre>;
};
class Main extends React.Component {
render(){
return (
<Router>
<Route path="*" render={layoutPicker}/>
</Router>
);
}
}
simple-layout.js and full-layout.js follow this format:
class SimpleLayout extends React.Component {
render(){
return (
<div>
<Route path="/signup" component={SignupPage}/>
<Route path="/login" component={LoginPage}/>
</div>
);
}
}
So, for this you should use render function (https://reacttraining.com/react-router/core/api/Route/render-func)
A really good article that helped me: https://simonsmith.io/reusing-layouts-in-react-router-4/
In the end you will be use something like this:
<Router>
<div>
<DefaultLayout path="/" component={SomeComponent} />
<PostLayout path="/posts/:post" component={PostComponent} />
</div>
</Router>
I solved this problem utilizing a bit of both of your solutions:
My Routes.js file
import BaseWithNav from './layouts/base_with_nav';
import BaseNoNav from './layouts/base_no_nav';
function renderWithLayout(Component, Layout) {
return <Layout><Component /></Layout>
}
export default () => (
<Switch>
{/* Routes with Sidebar Navigation */}
<Route exact path="/" render={() => renderWithLayout(Home, BaseWithNav)} />
{/* Routes without Sidebar Navigation */}
<Route path="/error" render={() => renderWithLayout(AppErrorMsg, BaseNoNav)} />
<Route path="/*" render={() => renderWithLayout(PageNotFound, BaseNoNav)} />
</Switch>
)
Base.js (where routes get imported)
export default class Base extends React.Component {
render() {
return (
<Provider store={store}>
<Router>
<Routes />
</Router>
</Provider>
)
}
}
Layouts
BaseWithNav.js
class BaseWithNav extends Component {
constructor(props) {
super(props);
}
render() {
return <div id="base-no-nav">
<MainNavigation />
<main>
{this.props.children}
</main>
</div>
}
}
export default BaseWithNav;
BaseNoNav.js
class BaseNoNav extends Component {
constructor(props) {
super(props);
}
render() {
let {classes} = this.props;
return <div id="base-no-nav">
<main>
{this.props.children}
</main>
</div>
}
}
export default BaseNoNav;
I hope this helps!
I know i am replying late but it's easy to do that, i hope it will helps to newbie.
i am using React 4
Layout.js
export default props => (
<div>
<NavMenu />
<Container>
{props.children}
</Container>
</div>
);
LoginLayout.js
export default props => (
<div>
<Container>
{props.children}
</Container>
</div>
);
Now finally we have our App
App.js
function renderWithLoginLayout(Component, Layout) {
return <LoginLayout><Component /></LoginLayout>
}
function renderWithLayout(Path, Component, Layout) {
return <Layout><Route path={Path} component={Component} /></Layout>
}
export default () => (
<Switch>
<Route exact path='/' render={() => renderWithLayout(this.path, Home, Layout)} />
<Route path='/counter' render={() => renderWithLayout(this.path, Counter, Layout)} />
<Route path='/fetch-data/:startDateIndex?' render={() => renderWithLayout(this.path, FetchData, Layout)} />
<Route path='/login' render={() => renderWithLoginLayout(Login, LoginLayout)} />
</Switch>
);