React Router not changing sub page - reactjs

In my App.js i have a navbar and a router switch that takes me to admin page
<Router>
<Navbar />
<Switch>
<Route exact path='/' component={Welcome} />
<AdminRoute path='/admin' component={Admin} />
My Admin component have a sub router as follows:
<Router>
<SubNav />
<Switch>
<Route exact path={`${match.path}/planners`} component={Planners} />
<Route exact path={`${match.path}/`} component = {Admin}>
</Switch>
</Router>
If i go to www.mysite/admin everything works fine and my SubNav links work fine. My issue is the my Navbar has links such as
<Typography
to='/admin/planners'
component={Link}
>
Planners
</Typography>
<Typography
to='/admin'
component={Link}
>
Admin
</Typography>
The moment i route to a subpage the Navbar links stop changing the subpages although they are changing the URL just fine. Linking from the Navbar at first time works then if i try clicking on Admin it does not route to / again.

I solved this by removing the <Switch> and removing the <Router> from the Admin component. <Route> alone without wrapping it with a <Router> was sufficient
Below is a code that explains the behavior
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
function Resource1({ match }) {
return (
<div>
<h3>Resource1</h3>
<p>Resource1 Descriotion</p>
</div>
);
}
function Resource2({ match }) {
return (
<div>
<h3>Resource2</h3>
<p>Resource2 Descriotion</p>
</div>
);
}
function Topic1({ match }) {
return (
<div>
<h2>Topic1</h2>
<p>Topic1 Description</p>
<ul>
<li>
<Link to={`${match.url}/resource1`}>Resource 1</Link>
</li>
<li>
<Link to={`${match.url}/resource2`}>Resource 2</Link>
</li>
</ul>
<hr />
<Route path={`${match.path}/resource1`} component={Resource1} />
<Route path={`${match.path}/resource2`} component={Resource2} />
</div>
);
}
function Topic2({ match }) {
return (
<div>
<h2>Topic2</h2>
<p>Topic2 Description</p>
<ul>
<li>
<Link to={`${match.url}/resource1`}>Resource 1</Link>
</li>
<li>
<Link to={`${match.url}/resource2`}>Resource 2</Link>
</li>
</ul>
<hr />
<Route path={`${match.path}/resource1`} component={Resource1} />
<Route path={`${match.path}/resource2`} component={Resource2} />
</div>
);
}
function Topics({ match }) {
return (
<div>
<h1>Topics</h1>
<ul>
<li>
<Link to={`${match.url}/topic1`}>Topic 1</Link>
</li>
<li>
<Link to={`${match.url}/topic2`}>Topic 2</Link>
</li>
</ul>
<hr />
<Route path={`${match.path}/topic1`} component={Topic1} />
<Route path={`${match.path}/topic2`} component={Topic2} />
</div>
);
}
function Home() {
return <h1>Home</h1>;
}
class App extends React.Component {
render() {
return (
<Router>
<div style={{ width: 1000, margin: '0 auto' }}>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
<li>
<Link to='/topics'>Topics</Link>
</li>
<li>
<Link to='/topics/topic1/resource1'>Resource 1</Link>
</li>
</ul>
<hr />
<Route exact path='/' component={Home} />
<Route path='/topics' component={Topics} />
</div>
</Router>
);
}
}
export default App;

Related

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

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

I should refresh browser to render my component using Routes | ReactJS

I have a menu with some routes. I want to show the component that correspond to his route while clicking on the menu. The issue is that, on the click, it changes me the url on my browser but the component displayed doesn't change. I have to refresh manually the page to render the good component. But my aim is to display the good component on the click without refreshing the page.
Here is my Menu.js code :
<Router>
<Link to={'/'} className="nav-link" id="homeLink">
<div className="tableElement active" id="home">
<FontAwesomeIcon icon={ faHome } className="icon"/>
</div>
</Link>
<Link to={'/org'} className="nav-link" id="orgLink">
<div className="tableElement" id="organisation">
<FontAwesomeIcon icon={ faTasks } className="icon"/>
</div>
</Link>
<Link to={'/users'} className="nav-link" id="usersLink">
<div className="tableElement" id="user">
<FontAwesomeIcon icon={ faUsers } className="icon"/>
</div>
</Link>
</Router>
Here is my render method App.js code :
render() {
return (
<Router>
<div className="App">
<LeftMenu/>
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/org'
render={ (routeProps) => ( <Organisations {...routeProps} /> ) }
/>
<Route path='/users'
render={ (routeProps) => ( <UserTable {...routeProps} /> ) }
/>
</Switch>
</Router>
);
}
Thanks for your help !
I resolved my issue, I just made a big mistake, my bad.
I just deleted <Router> tags that wrapped my code in Menu.js.
<Link to={'/'} className="nav-link" id="homeLink">
<div className="tableElement active" id="home">
<FontAwesomeIcon icon={ faHome } className="icon"/>
</div>
</Link>
<Link to={'/org'} className="nav-link" id="orgLink">
<div className="tableElement" id="organisation">
<FontAwesomeIcon icon={ faTasks } className="icon"/>
</div>
</Link>
<Link to={'/users'} className="nav-link" id="usersLink">
<div className="tableElement" id="user">
<FontAwesomeIcon icon={ faUsers } className="icon"/>
</div>
</Link>
I believed that <Link> tags should be wrapped by <Router> tags to work. So if you have the same issue, just check if you wrapped your <Link> tags by <Router> tags, and delete them.

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;

Resources