Why does my component keep rendering in all routes? - reactjs

I am attempting to render a component when I enter a url that does not exists. However, the component keeps rendering in all routes. I am using react-router-dom#4.1.1. This are the routes that I set up:
import * as React from "react";
import { Route, RouteComponentProps } from "react-router-dom";
import glamorous from "glamorous";
import ElementList from "./elementlist";
import AddElement from "./addelement";
import NotFound from "./NotFound";
const Styling = glamorous.div({
minHeight: 5,
minWidth: 8
});
const NavRouter = () => (
<Styling>
<Route path="/" exact={true} component={ElementList} />
<Route path="/addelement" component={(props:
RouteComponentProps<{}>) => (
<AddElement onSubmitSuccess={() => props.history.push("/")} />
)} />
<Route path="*" exact={true} component={NotFound}/>
</Styling>
);
export default NavRouter;
This is my NotFound component:
import * as React from "react";
const NotFound = () => (
<h1>The page that you are looking is not there.</h1>
);
export default NotFound;
The issue that I am currently facing is that the message: The page that you are looking is not there. keeps popping up on the / and /addelement route when I changed the URL. I am having a hard time trying to make the message appear only when I go to a route that is not defined. Initially, I tried to switch the routes and make the more "detailed" route at the top like this:
const NavRouter = () => (
<Styling>
<Route path="/addelement" component={(props:
RouteComponentProps<{}>) => (
<AddElement onSubmitSuccess={() => props.history.push("/")} />
)} />
<Route path="/" exact={true} component={ElementList} />
<Route path="*" component={NotFound}/>
</Styling>
);
However, it did not solve the issue. Is there a way to prevent the message from appearing on every route that I go to except for routes that are not defined?

You should use a <Switch> component. Per the documentation:
How is this different than just using a bunch of <Route>s?
<Switch> is unique in that it renders a route exclusively. In contrast, every <Route> that matches the location renders inclusively. Consider this code:
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
If the URL is /about, then <About>, <User>, and <NoMatch> will all render because they all match the path. This is by design, allowing us to compose <Route>s into our apps in many ways, like sidebars and breadcrumbs, bootstrap tabs, etc.
Occasionally, however, we want to pick only one <Route> to render. If we’re at /about we don’t want to also match /:user (or show our “404” page).
Thus, import it from react-router-dom:
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
Then apply it like so (note there is no need for path="*"):
<Switch>
<Route path="/" exact={true} component={ElementList} />
<Route path="/addelement" component={(props:
RouteComponentProps<{}>) => (
<AddElement onSubmitSuccess={() => props.history.push("/")} />
)} />
<Route component={NotFound}/>
</Switch>

Related

<Route component> and <Route children> in the same route

Warning: You should not use Route component and Route children in the same route; Route component will be ignored
import { BrowserRouter, Route, Switch } from "react-router-dom"
import { useState } from "react";
import SideBar from "./SideBar";
import Playing from "./Playing";
import "./App.css";
import AllSong from "./Components/AllSong";
import Favourites from "./Components/Favourites";
function App() {
const [sidebar, setSidebar] = useState(false);
return (
<BrowserRouter>
<div className="box image photo">
<SideBar sidebar={sidebar}/>
<Switch>
<Route path="/" exact component={Playing}>
<Playing sidebar={sidebar} setSidebar={(bool) => setSidebar(bool)}/>
</Route>
<Route path="/AllSong" component={AllSong}/>
<Route path="/Favourites" component={Favourites}/>
</Switch>
</div>
</BrowserRouter>
);
}
export default App;
Can someone explain why is the warning coming and also help to improve the code. (I'm new to react)
I tried using the v6 for react-router and react-router-dom b ut it showed number of errors so i degraded again and used switch again
Thanks You.
This is happening because you have <Route path="/" ...> & within that you have <Playing... so this confuses the Router & it doesn't know whether to render the parent <Route ... or the child <Playing... hence the error.
Like the warning says, you can't specify both a route component and wrap children components.
From the docs:
Warning: <Route children> takes precedence over both <Route component> and <Route render> so don’t use more than one in the
same <Route>.
The issue is specifically with:
<Route path="/" exact component={Playing}>
<Playing sidebar={sidebar} setSidebar={(bool) => setSidebar(bool)}/>
</Route>
You are rendering Playing in both places. Since it seems like you want to pass additional props into Playing then I suggest using the render prop so Playing can also still receive route props, i.e. history, location, and match props.
<Route
path="/"
exact
render={props => (
<Playing
{...props}
sidebar={sidebar}
setSidebar={(bool) => setSidebar(bool)}
/>
)}
/>
See Route render methods for further clarification on rendering routed content by a Route component.
it is specifically these lines:
<Route path="/" exact component={Playing}>
<Playing sidebar={sidebar} setSidebar={(bool) => setSidebar(bool)}/>
</Route>
you're setting both the component
component={Playing}
and the children
<Playing sidebar={sidebar} setSidebar={(bool) => setSidebar(bool)}/>
React will only render one of them so one of them has to be ignored, in this case the component.
If you remove one of them the warning will disappear.

React Router - Grouping routes in variables or components

I am trying to set up my router to handle a multilingual site, and having trouble with the modularization of the routes.
My routes file looks like this:
import React from 'react'
import { Redirect, Route, Switch, useRouteMatch } from 'react-router-dom'
import FrontPage from './views/front-view'
import SearchPortal from './views/search-portal-view'
import DocumentView from './views/document-view'
import ErrorView from './views/error-view'
export const Routes = (): JSX.Element => {
const { path } = useRouteMatch()
return (
<>
<Route path={`${path}/search`} component={SearchPortal} />
<Route path={`${path}/document/`} component={DocumentView} />
<Route path={`${path}/error`} component={ErrorView} />
<Route path={`${path}/`} exact component={FrontPage} />
<Route component={ErrorView} />
</>
)
}
export const DefaultRoutes = (
<>
<Route path='/search'>
{console.log('Search Route')}
<Redirect to='/en/search' />
</Route>
<Route path='/document'>
{console.log('document route')}
<Redirect to='/en/document' />
</Route>
<Route path='/error'>
{console.log('error route')}
<Redirect to='/en/error' />
</Route>
<Route path='/' exact>
{console.log('home route')}
<Redirect to='/en/' />
</Route>
</>
)
export const LangRoutes = (): JSX.Element => (
<Switch>
<Route path='/:lang([a-z]{2})' component={Routes} />
{DefaultRoutes}
<Route component={ErrorView} /> {/* Rendered when nothing else works */}
</Switch>
)
Everything works except for if I have an invalid URL without a language on it. (eg. localhost:3000/invalidPath). If the path becomes invalid after the language, it works fine, and the Error page is displayed.
If, however, I copy and paste the contents of the DefaultRoutes (without the Fragment) into LangRoutes, then everything works properly.
Why is this? And is there a better way to go about doing this?

React-Router - Route re-rendering component on route change

Please read this properly before marking as duplicate, I assure you I've read and tried everything everyone suggests about this issue on stackoverflow and github.
I have a route within my app rendered as below;
<div>
<Header compact={this.state.compact} impersonateUser={this.impersonateUser} users={users} organisations={this.props.organisations} user={user} logOut={this.logout} />
<div className="container">
{user && <Route path="/" component={() => <Routes userRole={user.Role} />} />}
</div>
{this.props.alerts.map((alert) =>
<AlertContainer key={alert.Id} error={alert.Error} messageTitle={alert.Error ? alert.Message : "Alert"} messageBody={alert.Error ? undefined : alert.Message} />)
}
</div>
The route rendering Routes renders a component that switches on the user role and lazy loads the correct routes component based on that role, that routes component renders a switch for the main pages. Simplified this looks like the below.
import * as React from 'react';
import LoadingPage from '../../components/sharedPages/loadingPage/LoadingPage';
import * as Loadable from 'react-loadable';
export interface RoutesProps {
userRole: string;
}
const Routes = ({ userRole }) => {
var RoleRoutesComponent: any = null;
switch (userRole) {
case "Admin":
RoleRoutesComponent = Loadable({
loader: () => import('./systemAdminRoutes/SystemAdminRoutes'),
loading: () => <LoadingPage />
});
break;
default:
break;
}
return (
<div>
<RoleRoutesComponent/>
</div>
);
}
export default Routes;
And then the routes component
const SystemAdminRoutes = () => {
var key = "/";
return (
<Switch>
<Route key={key} exact path="/" component={HomePage} />
<Route key={key} exact path="/home" component={HomePage} />
<Route key={key} path="/second" component={SecondPage} />
<Route key={key} path="/third" component={ThirdPage} />
...
<Route key={key} component={NotFoundPage} />
</Switch>
);
}
export default SystemAdminRoutes;
So the issue is whenever the user navigates from "/" to "/second" etc... app re-renders Routes, meaning the role switch logic is rerun, the user-specific routes are reloaded and re-rendered and state on pages is lost.
Things I've tried;
I've tried this with both react-loadable and React.lazy() and it has the same issue.
I've tried making the routes components classes
Giving all Routes down the tree the same key
Rendering all components down to the switch with path "/" but still the same problem.
Changing Route's component prop to render.
Changing the main app render method to component={Routes} and getting props via redux
There must be something wrong with the way I'm rendering the main routes component in the app component but I'm stumped, can anyone shed some light? Also note this has nothing to do with react-router's switch.
EDIT: I've modified one of my old test project to demonstrate this bug, you can clone the repo from https://github.com/Trackerchum/route-bug-demo - once the repo's cloned just run an npm install in root dir and npm start. I've got it logging to console when the Routes and SystemAdminRoutes are re-rendered/remounted
EDIT: I've opened an issue about this on GitHub, possible bug
Route re-rendering component on every path change, despite path of "/"
Found the reason this is happening straight from a developer (credit Tim Dorr). The route is re-rendering the component every time because it is an anonymous function. This happens twice down the tree, both in App and Routes (within Loadable function), below respectively.
<Route path="/" component={() => <Routes userRole={user.Role} />} />
needs to be
<Routes userRole={user.Role} />
and
loader: () => import('./systemAdminRoutes/SystemAdminRoutes')
Basically my whole approach needs to be rethought
EDIT: I eventually fixed this by using the render method on route:
<Route path="/" render={() => <Routes userRole={user.Role} />} />
Bumped into this problem and solved it like this:
In the component:
import {useParams} from "react-router-dom";
const {userRole: roleFromRoute} = useParams();
const [userRole, setUserRole] = useState(null);
useEffect(()=>{
setUserRole(roleFromRoute);
},[roleFromRoute]}
In the routes:
<Route path="/generic/:userRole" component={myComponent} />
This sets up a generic route with a parameter for the role.
In the component useParams picks up the changed parameter und the useEffect sets a state to trigger the render and whatever busines logic is needed.
},[userRole]);
Just put the "/" in the end and put the other routes above it.
Basically it's matching the first available option, so it matches "/" every time.
<Switch>
<Route key={key} exact path="/home" component={HomePage} />
<Route key={key} path="/second" component={SecondPage} />
<Route key={key} path="/third" component={ThirdPage} />
<Route key={key} exact path="/" component={HomePage} />
<Route key={key} component={NotFoundPage} />
</Switch>
OR
<Switch>
<Route path="/second" component={SecondPage} />
<Route exact path="/" component={HomePage} />
<Route path="*" component={NotFound} />
</Switch>
Reorder like this, it will start working.
Simple :)

Uncalled components keeps leaking into other components [duplicate]

I am attempting to render a component when I enter a url that does not exists. However, the component keeps rendering in all routes. I am using react-router-dom#4.1.1. This are the routes that I set up:
import * as React from "react";
import { Route, RouteComponentProps } from "react-router-dom";
import glamorous from "glamorous";
import ElementList from "./elementlist";
import AddElement from "./addelement";
import NotFound from "./NotFound";
const Styling = glamorous.div({
minHeight: 5,
minWidth: 8
});
const NavRouter = () => (
<Styling>
<Route path="/" exact={true} component={ElementList} />
<Route path="/addelement" component={(props:
RouteComponentProps<{}>) => (
<AddElement onSubmitSuccess={() => props.history.push("/")} />
)} />
<Route path="*" exact={true} component={NotFound}/>
</Styling>
);
export default NavRouter;
This is my NotFound component:
import * as React from "react";
const NotFound = () => (
<h1>The page that you are looking is not there.</h1>
);
export default NotFound;
The issue that I am currently facing is that the message: The page that you are looking is not there. keeps popping up on the / and /addelement route when I changed the URL. I am having a hard time trying to make the message appear only when I go to a route that is not defined. Initially, I tried to switch the routes and make the more "detailed" route at the top like this:
const NavRouter = () => (
<Styling>
<Route path="/addelement" component={(props:
RouteComponentProps<{}>) => (
<AddElement onSubmitSuccess={() => props.history.push("/")} />
)} />
<Route path="/" exact={true} component={ElementList} />
<Route path="*" component={NotFound}/>
</Styling>
);
However, it did not solve the issue. Is there a way to prevent the message from appearing on every route that I go to except for routes that are not defined?
You should use a <Switch> component. Per the documentation:
How is this different than just using a bunch of <Route>s?
<Switch> is unique in that it renders a route exclusively. In contrast, every <Route> that matches the location renders inclusively. Consider this code:
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
If the URL is /about, then <About>, <User>, and <NoMatch> will all render because they all match the path. This is by design, allowing us to compose <Route>s into our apps in many ways, like sidebars and breadcrumbs, bootstrap tabs, etc.
Occasionally, however, we want to pick only one <Route> to render. If we’re at /about we don’t want to also match /:user (or show our “404” page).
Thus, import it from react-router-dom:
import { Route, RouteComponentProps, Switch } from 'react-router-dom';
Then apply it like so (note there is no need for path="*"):
<Switch>
<Route path="/" exact={true} component={ElementList} />
<Route path="/addelement" component={(props:
RouteComponentProps<{}>) => (
<AddElement onSubmitSuccess={() => props.history.push("/")} />
)} />
<Route component={NotFound}/>
</Switch>

How to nest routes in React Router v4?

Is there a way to nest routes in React Router v4?
This works:
<Router basename='/app'>
<main>
<Route path='/' component={AppBar} />
<Route path='/customers' component={Customers} />
</main>
</Router>
This does not:
<Router basename='/app'>
<Route path='/' component={AppBar}>
<Route path='/customers' component={Customers} />
</Route>
</Router>
Customers Component:
import React, { Component, PropTypes } from 'react'
import styled from 'styled-components'
export default class Customers extends Component {
render () {
return (
<Container>
<h1>Customers</h1>
</Container>
)
}
}
const Container = styled.section`
height: 100%;
padding: 15px;
overflow: auto;
`
Best pattern I have found so far.
// main app
<div>
// not setting a path prop, makes this always render
<Route component={AppShell}/>
<Switch>
<Route exact path="/" component={Login}/>
<Route path="/dashboard" component={AsyncDashboard(userAgent)}/>
<Route component={NoMatch}/>
</Switch>
</div>
I can just keep nesting this inside a component and everything works nice including hmr(If using webpack, dont forget to set output.publicPath to "/")
// dashboard component
<div>
// the same way as before, not setting a path prop
// makes it render on every /dashboard/** request
<Route component={DashboardTAB}/>
<Switch>
// longer path (with same root) than others first
<Route path="/dashboard/graphs/longerpath" component={GraphForm}/>
<Route path="/dashboard/graphs" component={Graphs}/>
<Route path="/dashboard/workers" component={List}/>
<Route path="/dashboard/insert" component={InsertComponent}/>
</Switch>
</div>
I adapted this from the docs, seem to work so far. Probably missing something obvious, and yes it is not the v4 way but we need all the routes defined in one place.
function RouteNest(props){ return (
<Route exact={props.exact} path={props.path} render={ p => <props.component {...p} children={props.children}/> } />
)}
export const MainRoutes = props =>
<div className='content layout'>
<Route exact path="/" component={Landing}/>
<Route path={'/contact'} component={Contact}/>
<RouteNest path={'/thing'} component={CompoWithSub}>
<RouteNest path={'/thing/suba'} component={SubComponentA}/>
<RouteNest path={'/thing/subb'} component={SubComponentB}/>
</RouteNest>
</div>
export const CompoWithSub = props => <div>{props.children)</div>
You're AppBar component is in charge of rendering Customers. For customers to be called, you have to render the children of AppBar. Anything directly nested under AppBar is a child of AppBar.
import React from 'react';
const AppBar = ({ children }) => (
<div>
<header>
<h1> stuff </h1>
</header>
{children}
</div>
);
export default AppBar
Please note that only AppBar will render when you visit "/". AppBar and Customers will render when you visit "/customers".
If someone wants to have nested routes without typing prefix of wrapper route I've created something like this in TSX:
Imports:
import * as React from 'react';
import { Route, RouteComponentProps, RouteProps, Switch } from 'react-router-dom';
import Index from 'views/index';
import Login from 'views/login';
import NoMatch from 'views/no-match';
Interfaces:
interface INestedRoutes {
nested?: string;
}
interface INestedRoute extends RouteProps, INestedRoutes {}
NestedRoute and NestedRoutes wrapper:
class NestedRoutes extends React.Component<INestedRoutes> {
public render() {
const childrenWithProps = React.Children.map(this.props.children, (child) => {
return React.cloneElement(
child as React.ReactElement<any>, { nested: this.props.nested },
);
})
return childrenWithProps;
}
}
const NestedRoute: React.SFC<INestedRoute> = (props: INestedRoute) => {
return <Route path={`${props.nested}${props.path}`} component={props.component} />;
};
And routes with wrapper:
const MultiLanguage: React.SFC<RouteComponentProps<any>> = (props: RouteComponentProps<any>) => {
return (
<NestedRoutes nested={props.match.path} >
<NestedRoute path="/test" component={Login} />
<NestedRoute path="/no-match" component={NoMatch} />
</NestedRoutes>
);
};
export default (
<Switch>
<Route path="/:language" component={MultiLanguage}/>
<Route exact={true} path="/" component={Index} />
<Route path="/login" component={Login} />
<Route component={NoMatch} />
</Switch>
);
For nested routes there is a very simple way which i using.
Example main router is be like that
<Router history={history}>
<Switch >
<Route path="/" component={Home}></Route>
</Switch>
</Router>
Inside Home component using Nested Routing be like:
<div className="App">
<Navbar title="Home" links = { NavbarLinks }/>
{this.renderContentPage()}
</div>
renderContentPage will check the URL and render the nested route.
<Route exact path="/" component={Page1}></Route>
<Route exact path="/page1" component={Page1}></Route>
<Route exact path='/page2' component={Page2} />
So inside Home component page1 and page2 components rendered.
Route expects a single children i.e. a component.
It should not be a new Route.
What you can do is to include your nested routes inside your customers component.
Also make sure to remove exact inside the routes in customers component.

Resources