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>
Related
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>
)
}
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>
);
}
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
I am using react-router for programatic navigation in my app. All is fine expect for this new feature I want to add, that requires conditional routing.
I need to render, for instance profileRoutes when location is /users/profile and somethingElse when location is /users/details for instance. The code below works fine for now as I only have profileRoutes built. But I consolidated the routes into one, and need /users/* or something to that effect, such that that location would match all routes related to /users/...
<Route
children={({ location }) => (
<div>
{
location.pathname === "/users/profiles" ?
<profileRoutes></profileRoutes> :
<otherComponent/>
}
</div>
)}
/>
My question: Is there a react-router way to achieve this? like location.pathname.startswith.... sort of thing?
For your requirements, please use the parameter exact.
To match either /users or /users/profiles or /users/what-ever:
<Route path="/users" component={Users} />
To match only /users
<Route exact path="/users" component={Users} />
In principle, let do as follows:
In the main component, use Route without exact to point to
the component Users
In component Users, use Route again to match
sub-components of Users
Here is the example:
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
export function RouterExample() {
return (
<Router>
<div>
<ul>
<li>Main Menu:</li>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/users" component={Users} />
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Welcome home</h2>
</div>
);
}
function Users({ match }) {
return (
<div>
<h2>Welcome to Users</h2>
<ul>
<li>Users menu:</li>
<li>
<Link to={`${match.url}/profiles`}>User Profiles</Link>
</li>
<li>
<Link to={`${match.url}/settings`}>User Settings</Link>
</li>
</ul>
<Route exact path={`${match.path}`} component={UserHome} />
<Route path={`${match.path}/profiles`} component={UserProfiles} />
<Route path={`${match.path}/settings`} component={UserSettings} />
<Route path={`${match.path}/:section`} component={UserFooter} />
</div>
);
}
function UserHome() {
return (
<div>
<h3>UserHome Component</h3>
</div>
);
}
function UserProfiles() {
return (
<div>
<h3>UserProfiles Component</h3>
</div>
);
}
function UserSettings() {
return (
<div>
<h3>UserSettings Component</h3>
</div>
);
}
function UserFooter({ match }) {
return (
<div>
<hr />
UserFooter Component is always visible under /users/*
<br />
You are now at
<br />
path: {match.path}
<br />
match params: {match.params.section}
</div>
);
}
Live demo at codesandbox
Thanks for pointing me in the correct direction, ie using match!
I didn't need to modify my routes though (I like to keep my components separate and just import instead. I do have an exact tag on the root component, /.
Here's what worked for me:
<Route
children={({ match }) => (
<div>
{
match.path == "/users" ?
<profileRoutes></profileRoutes> :
<otherComponent/>
}
</div>
)}
/>
Now when match.path == "/users" is true, all routes downstream ie /users/etc are rendered.
Hope this helps someone else!
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;