React routes nesting on homepage - reactjs

I am able to get routes working on pages level, also nested routes working fine if not on the homepage.
e.g
/ ........ homepage
/pageA ... PageA
/pageA1.....PageA1 (nested)
/PageA2.....PageA2 (nested)
/PageB ... PageB
However, I am unable to have nested routes on homepage, the strucutre looks like below
/..........PageA(homepage)
/pageA1.....PageA1 (nested)
/PageA2.....PageA2 (nested)
/PageB ... PageB
Here is the code, the code can be played on codesandbox
const PageB1 = () => <>"Page B 1"</>;
const PageB2 = () => <>"Page B 2"</>;
const PageB3 = () => <>"Page B 3"</>;
const PageA = ({ match }) => {
return (
<>
<ul>
<li>
<Link to="/pagea1">Page A 1</Link>
</li>
<li>
<Link to="/pagea2">Page A 2</Link>
</li>
<li>
<Link to="/pagea3">Page A 3</Link>
</li>
</ul>
<hr />
<Route exact path={match.path + "/pagea1"} component={PageB1} />
<Route exact path={match.path + "/pagea2"} component={PageB2} />
<Route exact path={match.path + "/pagea3"} component={PageB3} />
</>
);
};
const PageB = () => " Page A";
export default function App() {
return (
<div className="App">
<Router>
<div>
<ul>
<li>
<Link to="/">Page A</Link>
</li>
<li>
<Link to="/pageb">Page B</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/" component={PageA} />
<Route path="/pageb" component={PageB} />
</Switch>
</div>
</Router>
</div>
);
}
This code is working fine for pageA(homepage) and pageB, but nested page Page A 1, Page A 2, Page A 3 are not working
If I remove exact from line 45 change it to <Route path="/" component={PageA} />
and remove match.path + from line 22-24 change the code to
<Route exact path={"/pagea1"} component={PageB1} />
<Route exact path={"/pagea2"} component={PageB2} />
<Route exact path={"/pagea3"} component={PageB3} />
Then the nested pages will work fine. But in the meantime, it breaks PageA(homepage), PageB.
Could someone please to advise where is the error I made? Thanks

Because the props path which you are passing to A component have value "/", and you are adding that value in the nested Route path i.e. //pagea1. so this is the reason it is not working.
one more thing i observed over here it is always suggested to add generic route in the last and here in your code you are add generic "/" route first.
follow the below code :-
const PageB1 = () => <>"Page B 1"</>;
const PageB2 = () => <>"Page B 2"</>;
const PageB3 = () => <>"Page B 3"</>;
const PageA = ({ match }) => {
return (
<>
<ul>
<li>
<Link to="/pagea1">Page A 1</Link>
</li>
<li>
<Link to="/pagea2">Page A 2</Link>
</li>
<li>
<Link to="/pagea3">Page A 3</Link>
</li>
</ul>
<hr />
/* match.path have "/" so you can remove it here
* because you are adding / in pagea1 */
<Route exact path={ "/pagea1"} component={PageB1} />
/* this syntax will work too because i have omit / from pageea2 value */
<Route exact path={match.path + "pagea2"} component={PageB2} />
<Route exact path={match.path + "pagea3"} component={PageB3} />
</>
);
};
const PageB = () => " Page A";
export default function App() {
return (
<div className="App">
<Router>
<div>
<ul>
<li>
<Link to="/">Page A</Link>
</li>
<li>
<Link to="/pageb">Page B</Link>
</li>
</ul>
<hr />
<Switch>
<Route path="/pageb" component={PageB} /> // keep specific paths
//route on top to generic
one
<Route path="/" component={PageA} /> // and in case of generic
you don't need exact
</Switch>
</div>
</Router>
</div>
);}
Hope it will work .

In this You code
exact keyword only render pagea for / url
when we click pagea1, now the url is /pagea1
so we don't see either pagea nor pageb
when you removed exact now every url start with / render pagea, in this case pageb won't work and /pagea1 url also stop works, because you are mentioning
path={match.path + "/pagea1"}
where math.path is / so //pagea1 will render pagea1.
Instead of removing exact and math.path you can create /pagea and redirect / to /pagea
Note: you have to make significant changes in PageA links
After that code will look like this
import { BrowserRouter as Router, Redirect, Route, Switch, Link } from "react-router-dom";
const PageB1 = () => <>"Page B 1"</>;
const PageB2 = () => <>"Page B 2"</>;
const PageB3 = () => <>"Page B 3"</>;
const PageA = ({ match }) => {
return (
<>
<ul>
<li>
<Link to="/pagea/pagea1">Page A 1</Link>
</li>
<li>
<Link to="/pagea/pagea2">Page A 2</Link>
</li>
<li>
<Link to="/pagea/pagea3">Page A 3</Link>
</li>
</ul>
<hr />
<Route exact path={match.path + "/pagea1"} component={PageB1} />
<Route exact path={match.path + "/pagea2"} component={PageB2} />
<Route exact path={match.path + "/pagea3"} component={PageB3} />
</>
);
};
const PageB = () => " Page B";
export default function App() {
return (
<div className="App">
<Router>
<div>
<ul>
<li>
<Link to="/">Page A</Link>
</li>
<li>
<Link to="/pageb">Page B</Link>
</li>
</ul>
<hr />
<Switch>
<Route path="/pagea" component={PageA} />
<Route path="/pageb" component={PageB} />
<Redirect to="/pagea" />
</Switch>
</div>
</Router>
</div>
);
}
PS: Hope I answered your question, if not let me know.

Related

Clearing url search params upon unmount

I'm using react router for routing in my app, and I can't seem to figure out how to use the useSearchParams hook in order to clear the search params upon navigating to a new route.
I've tried to utilize the cleanup function of useEffect in order to clean up the search params, but it seems to navigate to the previous route upon exiting the component.
The basic code is something like the following:
import * as React from "react";
import { Routes, Route, Outlet, Link, useSearchParams } from "react-router-dom";
export default function App() {
return (
<div>
<h1>Basic Example</h1>
<p>
Lorem ipsum
</p>
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="*" element={<NoMatch />} />
</Route>
</Routes>
</div>
);
}
function Layout() {
return (
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
<li>
<Link to="/nothing-here">Nothing Here</Link>
</li>
</ul>
</nav>
<hr />
<Outlet />
</div>
);
}
function About() {
const [searchParams, setSearchParams] = useSearchParams();
useEffect(() => {
return () => {
searchParams.delete("someParam")
setSearchParams(searchParams); // This will route back to the `About` component
}
}, setSearchParams)
return (
<div>
<h2>About</h2>
</div>
);
}
And I made a demo.
I've managed to overcome this issue by using the following browser native code, though it would still be nice if a more "react-router"-y solution was possible:
useEffect(() => () => {
const url = new URL(window.location);
url.searchParams.remove('key');
window.history.replaceState(null, '', url.toString());
})

React Router: Redirect to a different component

There are two main links on the landing page and then when user clicks on the option it should redirect to that component and not render the component below in the same page.
But, rather than going to the next page, the component is displayed below like a navbar.
const MainRoutes = () => {
return (
<ReactRouterDOM.HashRouter>
<div className='routes'>
<nav>
<ul>
<li>
<Link to='/team-member'>Team Member</Link>
</li>
<li>
<Link to='/moderator'>Moderator</Link>
</li>
</ul>
</nav>
<Route path='/team-member' component={TeamMember} />
<Route path='/moderator' component={Moderator} />
</div>
</ReactRouterDOM.HashRouter>
);
};
const App = () => {
return (
<div className='app'>
<MainRoutes />
</div>
);
};
You need to wrap your routes in Switch:
<Switch>
<Route exact path='/team-member' component={TeamMember} />
<Route exact path='/moderator' component={Moderator} />
</Switch>
As Switch renders the first child <Route> that matches the location. Also, it renders a route exclusively (which you need).
Edit:
If you don't want to show NavBar at other components but only at index route /, you can do this:
const MainRoutes = () => {
return (
<HashRouter>
<div className="routes">
<Switch>
<Route exact path="/" component={NavBar} />
<Route exact path="/team-member" component={TeamMember} />
<Route exact path="/moderator" component={Moderator} />
</Switch>
</div>
</HashRouter>
)
}
NavBar:
function NavBar() {
return (
<nav>
<ul>
<li>
<Link to="/team-member">Team Member</Link>
</li>
<li>
<Link to="/moderator">Moderator</Link>
</li>
</ul>
</nav>
)
}

Nested "Redirect" in React-Router-DOM

I'm new to React JS and now I'm trying to learn how to use "react-router-dom". Here I have a very simple app and I'm trying to fix one issue. So, there are 4 pages (Main Page / First Page / Second Page / Third Page wih Items ). We can ignore the first 3 pages and focus on the last one - Third Page wih Items. There we have 3 items. I used <Redirect /> to make First Item content visible immediately after users click on Third Page wih Items and it seems to work fine but there is a problem... First time you click on Third Page wih Items, First Item is shown as expected. But if you are still inside Third Page wih Items and you click on it again, First Item disappears and you basically need to reload the page or go to another page and back.
My question is - What should I do make First Item stays even if users click on Third Page wih Items multiple times in a row?
import React from "react";
import { Route, NavLink, Redirect } from "react-router-dom";
import "./App.css";
const Header = () => {
return (
<ul>
<li>
<NavLink to="/">Main Page</NavLink>
</li>
<li>
<NavLink to="/first">First Page</NavLink>
</li>
<li>
<NavLink to="/second">Second Page</NavLink>
</li>
<li>
<NavLink to="/third-with-items">Third Page wih Items</NavLink>
</li>
</ul>
);
};
const Main = () => <h1>Main Page</h1>;
const First = () => {
return <h3>First Page Content</h3>;
};
const Second = () => {
return <h3>Second Page Content</h3>;
};
const Third = () => {
return (
<div>
<div>
<h3>Third Page Content</h3>
<ul>
<li>
<NavLink to="/third-with-items/item1">First Item</NavLink>
</li>
<li>
<NavLink to="/third-with-items/item2">Second Item</NavLink>
</li>
<li>
<NavLink to="/third-with-items/item3">Third Item</NavLink>
</li>
</ul>
</div>
<Redirect to="/third-with-items/item1" />
<Route path="/third-with-items/item1" component={FirstItem} />
<Route path="/third-with-items/item2" component={SecondItem} />
<Route path="/third-with-items/item3" component={ThirdItem} />
</div>
);
};
const FirstItem = () => (
<div>This text should be shown after you click "Third Page with Items"</div>
);
const SecondItem = () => <div>Something...</div>;
const ThirdItem = () => <div>Another something...</div>;
const App = () => {
return (
<div>
<Header />
<Route exact path="/" component={Main} />
<Route path="/first" component={First} />
<Route path="/second" component={Second} />
<Route path="/third-with-items" component={Third} />
</div>
);
}
export default App;
Replace:
<Redirect to="/third-with-items/item1" />
with:
<Route exact path="/third-with-items" component={FirstItem} />
The React Router documentation shows an example of nesting routes that may help you improve your component:
import {BrowserRouter, Route, NavLink, Switch, useRouteMatch} from "react-router-dom";
const Third = () => {
let { path, url } = useRouteMatch();
return (
<div>
<div>
<h3>Third Page Content</h3>
<ul>
<li>
<NavLink to={`${url}/item1`}>First Item</NavLink>
</li>
<li>
<NavLink to={`${url}/item2`}>Second Item</NavLink>
</li>
<li>
<NavLink to={`${url}/item3`}>Third Item</NavLink>
</li>
</ul>
</div>
<Switch>
<Route exact path={path} component={FirstItem} />
<Route path={`${path}/item1`} component={FirstItem} />
<Route path={`${path}/item2`} component={SecondItem} />
<Route path={`${path}/item3`} component={ThirdItem} />
</Switch>
</div>
);
};

how to get route param values of child component from parent component in app using React Router

if I have nested routes, /:foo/:bar, how would I get access to match.params.bar from the component rendering /:foo? Is context the most common way to do this or...? Thanks.
Don't mind this paragraph. It's just some filler to fulfill the arbitrarily set quota fjewoifjoai;fiwajfoijaweoifjiowfjoiawjfioawjfwafaweffa
I have an example here as well:
const Header = withRouter(({ match }) => {
console.log("match>>>", match.params); // what's the most common way for this to have
// the 'childest' param value?
return (
<header>
<nav id="nav" role="navigation">
<ul>
<li>
<NavLink to="/">Home</NavLink>
</li>
<li>
<NavLink to="/first">First</NavLink>
</li>
<li>
<NavLink to="/second">Second</NavLink>
</li>
<ul>
<li>
<NavLink to="/second/1">Second -> 1</NavLink>
</li>
<li>
<NavLink to="/second/2">Second -> 2</NavLink>
</li>
</ul>
</ul>
</nav>
</header>
);
});
const HomePage = () => {
return <h1>HomePage</h1>;
};
const NumPage = props => {
const num = props.match.params.no;
console.log("num>>", props.match.params, props.match.path);
if (num === "second") {
return (
<div>
<h1>{num}</h1>
<Switch>
<Route path={`${props.match.path}/:sub`} component={SubPage} />
</Switch>
</div>
);
}
return <h1>{num}</h1>;
};
const SubPage = props => {
return (
<h1>
{props.match.params.no} --> {props.match.params.sub}
</h1>
);
};
const App = () => (
<BrowserRouter>
<div>
<Header />
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/:no" component={NumPage} />
</Switch>
</div>
</BrowserRouter>
);
You can extend your first Route to check for both the foo as well as the bar parameter like this:
<Route path="/:foo/:bar?" component={NumPage} />
Now your match.params object contains a foo and bar field with the respective values.
You can access these within your child components easily and you dont have duplicated logic with nested routes. By adding the ?, bar becomes optional so that the route "/:foo" is still valid.
Hope this helps. Happy coding.

React - How to prevent <a href> from refreshing

I have an that links to another stateless component.
I have an onClick listener that calls a method that calls e.preventDefault(), but this just makes the not link to anywhere when clicked.
constructor(props, context) {
super(props, context);
this.preventRefresh = this.preventRefresh.bind(this);
}
<a href={/components/Button'} onClick={this.preventRefresh}>{n.componentName}</a>
preventRefresh(e) {
e.preventDefault();
}
So clicking on the does nothinh. How can I prevent the page from reloading?
In React, this doesn't works:
<a href={/components/Button'} onClick={this.preventRefresh}>{n.componentName}</a>
You can't set the href attribute to a component (a component is not a URL)
If you want to make a navigation link, you should use react-router-dom (if you are working for browsers):
Fist, you have to install it:
npm install --save react-router-dom
Then you can use it, check the official example:
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
const BasicExample = () => (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</Router>
);
const Home = () => (
<div>
<h2>Home</h2>
</div>
);
const About = () => (
<div>
<h2>About</h2>
</div>
);
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${match.url}/rendering`}>Rendering with React</Link>
</li>
<li>
<Link to={`${match.url}/components`}>Components</Link>
</li>
<li>
<Link to={`${match.url}/props-v-state`}>Props v. State</Link>
</li>
</ul>
<Route path={`${match.url}/:topicId`} component={Topic} />
<Route
exact
path={match.url}
render={() => <h3>Please select a topic.</h3>}
/>
</div>
);
const Topic = ({ match }) => (
<div>
<h3>{match.params.topicId}</h3>
</div>
);
export default BasicExample;
And check the docs here

Resources