React Router: Redirect to a different component - reactjs

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>
)
}

Related

React routes nesting on homepage

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.

When matching url nested router not working

I have tried for several hours now but without success to identify the reason why this nested router does not work. In the following example in the main component a series of links are listed, among them /surveys that when matching will show the <Surveys /> component, within this component when the url matches /surveys/:id the <Answers /> component should be shown but it never shows. Could you help me identify the reason for this behavior
import ...
function App() {
// some logic
return (
<div>
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/employees">Employees</Link>
</li>
<li>
<Link to="/surveys">Surveys</Link>
</li>
<li>
<Link to="/survey-creator">Survey creator</Link>
</li>
</ul>
</nav>
</div>
<Switch>
<Route path="/survey-creator">
<MySurveyCreator />
</Route>
<Route path="/employees">
<Employees employees={state.employees} />
</Route>
<Route path="/surveys" exact={true}>
<Surveys />
</Route>
<Route path="/" exact={true}>
<Home />
</Route>
</Switch>
</Router>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
Surveys
import ...
function SingleSurvey({ id, name }) {
return (
<li>
<Link to={`/surveys/${id}`}>{name}</Link>
</li>
);
}
export default function Surveys() {
// some logic
return (
<div>
<h1>Survey</h1>
<Switch>
<ul>
{surveys.map(survey => (
<SingleSurvey key={survey.id} {...survey} />
))}
</ul>
<Route exact={true} path={`/${match.path}/:id`}>
<Answers />
</Route>
</Switch>
</div>
);
}
Answers
import React from "react";
import { useParams } from "react-router-dom";
export default function Answers() {
const { id } = useParams();
console.log(id);
return (
<div>
<h1>Answer</h1>
<h2>Current id is: {id}</h2>
</div>
);
}
Your nested route is in Serveys Component, So The Serveys Component has to match the url for the nested route but you put an exact keyword for it that won't match with the /surveys/:id
So in result you won't have a Serveys Component that renders <Answers /> Component.
No Servey No Answer :)
just change this:
<Route path="/surveys" exact={true}>
<Surveys />
</Route>
to this:
<Route path="/surveys">
<Surveys />
</Route>

react js router route to outside router in nested router

I have a problem with nested react-router-dom. I want to get out of inner router to outer router. I don't know how to explain it, so I bring a example here.
https://codesandbox.io/s/react-router-preventing-transitions-forked-vmt4c?fontsize=14&hidenavigation=1&theme=dark
What I want to do here is route back to '/' (BlockingForm component) from Topics component by clicking go root button.
How can I solve this?
import React, { useState } from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Prompt,
useParams,
useRouteMatch
} from "react-router-dom";
// Sometimes you want to prevent the user from
// navigating away from a page. The most common
// use case is when they have entered some data
// into a form but haven't submitted it yet, and
// you don't want them to lose it.
export default function PreventingTransitionsExample() {
return (
<Router>
<ul>
<li>
<Link to="/">Form</Link>
</li>
<li>
<Link to="/home">home</Link>
</li>
<li>
<Link to="/one">One</Link>
</li>
<li>
<Link to="/two">Two</Link>
</li>
</ul>
<Switch>
<Route path="/" exact children={<BlockingForm />} />
<Route path="/home" exact children={<NestingExample />} />
<Route path="/one" children={<h3>One</h3>} />
<Route path="/two" children={<h3>Two</h3>} />
</Switch>
</Router>
);
}
function NestingExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
<li>
<Link to="home/topics">Topics</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/home/">
<Home />
</Route>
<Route path="/home/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>
<li>
<Link to={"/"}>Go root</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>
);
}
function BlockingForm() {
let [isBlocking, setIsBlocking] = useState(false);
return (
<form
onSubmit={(event) => {
event.preventDefault();
event.target.reset();
setIsBlocking(false);
}}
>
<Prompt
when={isBlocking}
message={(location) =>
`Are you sure you want to go to ${location.pathname}`
}
/>
<p>
Blocking? {isBlocking ? "Yes, click a link or the back button" : "Nope"}
</p>
<p>
<input
size="50"
placeholder="type something to block transitions"
onChange={(event) => {
setIsBlocking(event.target.value.length > 0);
}}
/>
</p>
<p>
<button>Submit to stop blocking</button>
</p>
</form>
);
}
You can achieve that by using the useHistory to manipulate the parent router (for react router v5)
reference:
https://v5.reactrouter.com/web/api/Hooks/usehistory
I've changed only the nested router in order to show you how to change the parent router location :)
import React, { useState } from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
Prompt,
useParams,
useRouteMatch,
useHistory
} from "react-router-dom";
export default function PreventingTransitionsExample() {
return (
<Router>
<ul>
<li>
<Link to="/">Form</Link>
</li>
<li>
<Link to="/home">home</Link>
</li>
<li>
<Link to="/one">One</Link>
</li>
<li>
<Link to="/two">Two</Link>
</li>
</ul>
<Switch>
<Route path="/" exact children={<BlockingForm />} />
<Route path="/home" exact children={<NestingExample />} />
<Route path="/one" children={<h3>One</h3>} />
<Route path="/two" children={<h3>Two</h3>} />
</Switch>
</Router>
);
}
function NestingExample() {
//use useHistory hook to get the history context from parent router
const history = useHistory();
//use this function on a onClick event instead of <link> to change the parent router
const changeParentRouter = (url) => history.push(url);
return (
<Router>
<div>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
<li>
<Link to="home/topics">Topics</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/home/">
<Home />
</Route>
<Route path="/home/topics">
<Topics />
</Route>
</Switch>
</div>
</Router>
);
}
For react router v6, use the useNavigate hook:
import { useNavigate } from "react-router-dom";
function useLogoutTimer() {
const userIsInactive = useFakeInactiveUser();
const navigate = useNavigate();
return <div>
<button onClick={() => navigateTo('/')}>
Dashboard
</button>
</div>
}
Reference: https://reactrouter.com/en/6.4.4/hooks/use-navigate

Rendering part of component only on few routes

I have my application and I'm using react-router.On every route I have rendered Navbar and in the Navbar I have input field which I want to render only on two routes. How can I do it ? Is there any "If" that can I use to render part of component when route is matched ?
In your Route you will have a collection of RouteParams (match, location and history). You can use e.g. the location.pathname to conditionally render whatever you want.
Something like:
<Router>
<Route render={({match,location,history})=>{
const showNavFields = location.pathname==='/your-path'
return {
<NavBar showNavFields={showNavFields}/>
}
}}/>
<Switch>
<Route path="/your-path" component="AComponent"/ >
<Route path="/your-other-path" component="AnotherComponent"/ >
</Switch>
</Router>
We can use match.url for that.
Working code. Here we are appending route matched to router url if route is /topics/rendering
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/topics" component={Topics} />
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
function Topics({ match }) {
return (
<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>
</ul>
<Route path={`${match.path}/:topicId`} component={Topic} />
<Route
exact
path={match.path}
render={() => <h3>Please select a topic.</h3>}
/>
</div>
);
}
function Topic({ match }) {
if (match.url === '/topics/rendering') {
return (
<div>
<h3>route matched {match.params.topicId}</h3>
</div>
)
} else {
return (
<div>
<h3>{match.params.topicId}</h3>
</div>
)
}
}
export default BasicExample;

Nested route not being rendered

I have:
<Switch>
<Route
exact
path={'/path/to/my/component'}
component={MyComponent}
/>
...
</Switch>
MyComponent:
return (
<div>
<h1>Here Some Text</h1>
<Link to={'/path/to/my/component/test'}>Test</Link>
<Route exact path={"/path/to/my/component/test"} component={MyOtherComponent} />
</div>
)
I am able to render MyComponent, but when I click on the link to .../test route it does not render the route below. It goes to a 404 page that I have defined.
Am i missing something?
--
So, after testing some answers, i got a problem that, the route that the link is redirecting to, does not display.
Given the following code (be aware that, all this code is already a route and is inside a <switch>).
render() {
const { match } = this.props;
return (
<div className="advanced-configuration">
<div className="advanced-configuration__content userManagement__body">
<Link to={`${match.url}/test`}>Test</Link>
<Route exact path={`${match.url}/test`} component={() => <h1>test123</h1>} />
<Route exact path={match.url} component={() => <h2>Hi from main compoponent</h2>} />
</div>
</div>
);
}
}
The statement: "Hi from main component" Gets loaded as i arrive in this route, but as i click on the test link, it falls into my "404" route, which is:
<Route component={NotFound} />
This NotFound route is sibling of MyComponent, and it is in the end of the root switch, the first one that i posted on this question.
What else can i look into, to try to see what is breaking this link?
Have you tried using match.url. Something like this. Here is what documentation says about it.
const Topics = ({ match }) => (
<div>
<ul>
<li>
<Link to={`${match.url}/rendering`}>Rendering with React</Link>
</li>
<li>
<Link to={`${match.url}/components`}>Components</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>
);
Have you tried something like :
let match = this.props.match
<div>
<h1>Here Some Text</h1>
<Link to={match.url + '/test'}>Test</Link>
<Route exact path={match.url + "/test"} component={MyOtherComponent} />
</div>
In your updated question, I am able to see the rendered JSX by clicking on the link using this markup :
<Link to={match.url + '/test'}>Show Test!</Link>
<Route path={match.url + "/test"} component={() => <h1>test123</h1>} />

Resources