React router displaying component when it shouldn't - reactjs

Based on my understanding, once you have implemented React Router, your website should not display any components unless the Route path matches the URL. However, my code is not behaving that way. I have things implemented, but the Invoices component is still wanting to render, even though the URL is '/'. How do I fix this problem?
UPDATED to prevent components from rendering and to show more code for LeftMenu
I'm still confused on whether or not I even need to use React Router for my situation.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './pages/App';
import { BrowserRouter as Router } from 'react-router-dom';
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root'),
);
App.js
return (
<>
<GlobalStyle />
<TopMenu
setDisplay={setDisplay}
display={display}
openNav={openNav}
closeNav={closeNav}
/>
<Wrapper>
<LeftMenu changeSection={changeSection} setSection={setSection} />
<MainStyle>
<BreadCrumbs
changeSection={changeSection}
number={number}
month={month}
breadCrumbs={breadCrumbs}
setSection={setSection}
/>
<Portal>
{section === 'home' && <Home />}
{section === 'invoice' && (
<Invoice>
<Invoices
invoices={invoices}
updateNumber={updateNumber}
updateMonth={updateMonth}
number={number}
month={month}
download={download}
/>
</Invoice>
)}
{section === 'act' && <ACT />}
{section === 'external' && <External />}
</Portal>
</MainStyle>
</Wrapper>
</>
);
LeftMenu.js
const LeftMenu = ({ changeSection }) => {
return (
<NavWindow>
<ul>
<li>
<Link to='/Employer Invoices'>Employer Invoices</Link>
</li>
<li>
<Link to='/ACT'>Enroll Employee</Link>
</li>
<li>
<Link to='/ACT'>Terminate Employee</Link>
</li>
<li>
<Link to='/ACT'>Employee Changes</Link>
</li>
</ul>
<Switch>
<Route exact path='/' component={Home} />
<Route path='/Employer Invoices' component={Invoices} />
<Route path='/Employer Invoices/:invoiceId' component={Invoices} />
<Route path='/Enroll Employee' component={ACT} />
<Route path='/Terminate Employee' component={ACT} />
<Route path='/Employee Changes' component={ACT} />
</Switch>
</NavWindow>
);
};

First, there is no link between your LeftMenu component and you App component here. Your leftMenu will never be rendered, and your App will always render all it contains(Home, Invoice, ACT, External...).
Second, the following assertion:
once you have implemented React Router, your website should not display any components unless the Route path matches the URL
is false.
In your context, if you change your index.js code from that:
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById('root'),
);
to that:
ReactDOM.render(
<Router>
<LeftMenu />
</Router>,
document.getElementById('root'),
);
I guess that it will get close to what you want. It will always render your NavWindow. Links inside will always be rendered, and above the ul tag will be rendered the component whose route match current path.
Here is a fiddle with you code and some modification to make it work, I let you discover the differences. Don't hesitate if you want more explanations.
https://jsfiddle.net/6atxpr15/

You need to wrap App in a Route component (https://reacttraining.com/react-router/web/api/Route) :
import { BrowserRouter as Router, Route } from 'react-router-dom'
ReactDOM.render(
<Router>
<Route path='/' component={App} />
</Router>
document.getElementById('root'),
);

Related

Why do my Link To links not work in React-Router?

Trying to create an about page for a website im working on, I found this solution on Stack but it does not work for me. I was using an outdated tutorial for my original code, this is my current code:
About.js:
import React from "react";
import { Link, Route, useMatch } from "react-router-dom";
import SinglePage from "./SinglePage";
const About = () => {
//const match = useMatch('/');
return (
<div className="about__content">
<ul className="about__list">
<li>
<Link to={'about-app'}>About App</Link>
</li>
<li>
<Link to={'about-author'}>About Author</Link>
</li>
</ul>
<Route path={':slug'}>
<SinglePage />
</Route>
</div>
);
};
export default About;
Index.js where I am rendering the component:
import React from "react";
import ReactDOM from "react-dom";
import TodoContainer from "./functionBased/components/TodoContainer"; // Component file
import "./functionBased/App.css"; // Style sheet
import { HashRouter as Router, Routes, Route } from "react-router-dom"; // Router file
import About from "./functionBased/pages/About";
import NotMatch from "./functionBased/pages/NotMatch";
ReactDOM.render(
<React.StrictMode>
<Router>
<Routes>
<Route exact path="/" element={<TodoContainer />} />
<Route path="/about/*" element={<About />} />
<Route path="*" element={<NotMatch />} />
</Routes>
</Router>
</React.StrictMode>,
document.getElementById("root")
);
Issues
The About component is directly rendering a Route component. The Route component can only be rendered by a Routes component or another Route component as a nested route.
The react-router-dom#6 Route components render their content on the element prop.
Solution
Import the Routes component and wrap the descendent Route component rendered by `About.
Render SinglePage on the route's element prop.
Example:
import React from "react";
import { Link, Routes, Route } from "react-router-dom";
import SinglePage from "./SinglePage";
const About = () => {
return (
<div className="about__content">
<ul className="about__list">
<li>
<Link to="about-app">About App</Link>
</li>
<li>
<Link to="about-author">About Author</Link>
</li>
</ul>
<Routes>
<Route path=":slug" element={<SinglePage />} />
</Routes>
</div>
);
};
export default About;
Alternative
You could alternatively move the SinglePage route out to the main router as a nested route (instead of where it is as a descendent route).
Example:
import React from "react";
import { Link, Outlet } from "react-router-dom";
import SinglePage from "./SinglePage";
const About = () => {
return (
<div className="about__content">
<ul className="about__list">
<li>
<Link to="about-app">About App</Link>
</li>
<li>
<Link to="about-author">About Author</Link>
</li>
</ul>
<Outlet />
</div>
);
};
export default About;
...
<Router>
<Routes>
<Route path="/" element={<TodoContainer />} />
<Route path="/about" element={<About />}>
<Route path=":slug" element={<SinglePage />} />
</Route>
<Route path="*" element={<NotMatch />} />
</Routes>
</Router>
You are defining the routes with /about/* and accessing them with about-something which does not exist at all, add \about\author in to for Link.

React execute common code on each route change

I have a component included in my App.js and that component contains some common logic which needs to be executed.
So my App component JSX looks like
<CommonComponent />
{canRenderBool && (
<div class="container">
<Route exact path="/comp1">
<Comp1 />
</Route>
<Route exact path="/comp2">
<Comp2 />
</Route>
</div>
)
}
Now, I want that on each route transition (e.g. user clicks on a new route url), I want that the code in CommonComponent (non-routable component) gets triggered.
This common component has logic which returns a boolean variable and I kind of render/not render Comp1/Comp2 i.e. all the routes based on that boolean
What is the best way I can handle it. I want to avoid having that code defined/invoked in each component manually?
Also most of my components are functional/hooks based.
In case you are using functional component for your App Component. You can write your CommonComponent logic inside useEffect and then there you can set state for your canRenderBool flag
for example
import React, { useState, useEffect } from "react";
import { BrowserRouter as Router, Switch, Route, Link, useLocation } from "react-router-dom";
function App() {
const [canRenderBool , setCanRenderBool] = useState(false);
const location = useLocation(); // this will only work if you wrap your App component in a Router
// this effect will run on every route change
useEffect(() => {
// your logic here
// ..
// set value of 'canRenderBool' flag
setCanRenderBool(true);
}, [location]);
return (
// Note that Router is not used here it will be wraped when we export this component, see below
<Switch>
<Route exact path="/">
<Page1 />
</Route>
{canRenderBool && (
<Route exact path="/page-2">
<Page2 />
</Route>
)}
</Switch>
);
}
function Page1() {
return (
<div>
<h1>
Go to <Link to="/page-2">Page 2</Link>
</h1>
</div>
);
}
function Page2() {
return (
<div>
<h1>
Go to <Link to="/">Page 1</Link>
</h1>
</div>
);
}
// Wrapping Router around App and exporting
export default () => (
<Router>
<App />
</Router>
);
{canRenderBool && (
<Router>
<div class="container">
<Switch>
<Route exact path="/comp1"> <Comp1 /> </Route>
<Route exact path="/comp2"> <Comp2 /> </Route>
</Switch>
<Link to="/comp1">Component 1</Link>
<Link to="/comp2">Component 2</Link>
</div>
</Router>
)
}
This might help. And you will need to import all keywords and components before using them.

how to use router inside a routed component

My App.js:
<Router>
<Header/>
<Switch>
<Route exact path="/" component={HomeScreen} />
<Route exact path="/screenOne" component={OneScreen} />
<Route exact path="/screenTwo" component={TwoScreen} />
</Switch>
</Router>
The <Header /> has three links to the respective components viz. HomeScreen, OneScreen, TwoScreen.
I want my <TwoScreen /> to be exactly like this baseComponent(i.e App.js) where I have some links and when I click those links, the components corresponding to the link/path gets rendered.
What is the best way to approach this?
there is an example on the react-router site that meets your need, under nesting, you can check it out https://reactrouter.com/web/example/nesting (for just web)
But for me, I simply declare them all in one place/component, especially on react-router-native, if you're doing react-native.
below is the example from their website for web
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useParams,
useRouteMatch
} from "react-router-dom";
// Since routes are regular React components, they
// may be rendered anywhere in the app, including in
// child elements.
//
// This helps when it's time to code-split your app
// into multiple bundles because code-splitting a
// React Router app is the same as code-splitting
// any other React app.
export default function NestingExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/topics">
<Topics />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function Topics() {
// The `path` lets us build <Route> paths that are
// relative to the parent route, while the `url` lets
// us build relative links.
let { path, url } = useRouteMatch();
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${url}/rendering`}>Rendering with React</Link>
</li>
<li>
<Link to={`${url}/components`}>Components</Link>
</li>
<li>
<Link to={`${url}/props-v-state`}>Props v. State</Link>
</li>
</ul>
<Switch>
<Route exact path={path}>
<h3>Please select a topic.</h3>
</Route>
<Route path={`${path}/:topicId`}>
<Topic />
</Route>
</Switch>
</div>
);
}
function Topic() {
// The <Route> that rendered this component has a
// path of `/topics/:topicId`. The `:topicId` portion
// of the URL indicates a placeholder that we can
// get from `useParams()`.
let { topicId } = useParams();
return (
<div>
<h3>{topicId}</h3>
</div>
);
}

How to navigate on a page without reloading the page

I have navigation in react and want to redirect to the listing page on click.using this right now which is loading the page
This is my Header.js file
return (
<Link to="/allusers">All Users</Link>
);
This is my App.js file
I imported this
import UsersList from './user/UsersList'; //then i defined
class App extends Component {
render () {
return (
<BrowserRouter>
<div>
<Header />
<Switch>
<Route exact path='/userlist' component={UsersList} />
</Switch>
</div>
</BrowserRouter>
)
}
}
You can check react-router or #reach/router
For example, in #reach/router, you can use the provided Link component to create the same anchor as in your example:
<Link to="/userlist">All Users</Link>
And create a router with all your routes:
<Router primary={false}>
<Home path="/" />
<AllUsers path="/allusers" />
<NotFound default />
</Router>
https://github.com/reach/router
You can play around with this example: https://reach.tech/router/tutorial/04-router
Same thing can be done with react-router.
This achieved through a client side routing: manipulation of the history object of the browser through client side
This is an example rendering a specific component for specific route
import { BrowserRouter, Route, Link, Switch } from "react-router-dom"
<BrowserRouter>
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/allusers" component={AllUsers} />
<Route component={NotFoundPage} />
</Switch>
</BrowserRouter>
// then just pair it up with a navigation bar
<Link to="/">All Users</Link>
<Link to="/allusers">All Users</Link>
These components tied up to a route has access to history object as prop where you can call history.push('/allusers') for other use cases
reference:
https://reacttraining.com/react-router/web/guides/quick-start
You can do that as follows:
goToUserList = () => {
this.props.history.push('/userlist')
}
...
return(
<button onClick={this.goToUserList}>Go User List</button>
)
Hope it helps.

React Routing still showing root component even after using exact

I am still a newbie to React. So here I am rendering the root component with two routes: Home and About located in functional components: home.js and about.js respectively. However, even after using exact attribute and , the root component keeps on rendering above. I still cannot figure out how to not render the root component when I am redirecting to any of the mentioned routes?
Heres the live demo: https://codesandbox.io/s/vmz6zwq0k7
The Route component is acting like a "placeholder" for the component you want to render when the URL matches. everything above it (parents and siblings) wont get affected.
Given this code example:
render() {
return (
<BrowserRouter>
<div className="App">
<Link to="/home"> Home </Link>{" "}
|
<Link to="/about"> About Us </Link>{" "}
<div>
<Route exact path="/home" component={Home} />
<Route exact path="/about" component={About} />
</div>
</div>
</BrowserRouter>
);
}
This line of code:
<Route exact path="/home" component={Home} />
Is only a "placeholder" for the Home component. It won't render anything only when the path is matching "/home".
When the path will match, the Route component will render the passed component, The Home component in this case.
It will not affect the entire app tree, and for a good reason!
If the entire app would get re-rendered and replaced with the Home component you would loose the navigation links.
I had the same problem looking at the react-routing getting started portion here. https://reactrouter.com/web/guides/quick-start
I placed my Router/BrowserRouter in my App component. Instead place the router in your index.js file like so
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
Then your app component can look like so and the root route wont be matched if about or users is matched.
import React from "react";
import {
Switch,
Route,
Link
} from "react-router-dom";
export default function App() {
return (
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
</nav>
{/* A <Switch> looks through its children <Route>s and
renders the first one that matches the current URL. */}
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
);
}
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
function Users() {
return <h2>Users</h2>;
}

Resources