React - How to prevent <a href> from refreshing - reactjs

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

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

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.

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

React Router not changing sub page

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;

How to get withRouter to pass match params to component?

I want to access the match params on the navigation on my app, I'm using react and react-router-dom. Here is a code snipped for simple example I made, as you can see on the Topics component I get the correct match at component level but not in the nav bar, I do get the correct url path in the location prop so I'm not sure if I'm doing this right.
This would be the working snippet, since I couldn't add react-router-dom to the stack overflow snippet.
import { BrowserRouter as Router, Route, Link, withRouter } from "react-router-dom";
const BasicExample = (props) =>
<Router>
<div>
<Nav/>
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/topics" component={Topics} />
</div>
</Router>;
const About = () => (
<div>
<h2>About</h2>
</div>
);
const Home = () => (
<div>
<h2>Home</h2>
</div>
);
const Navigation = (props) => (
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
<li>{`match prop -> ${JSON.stringify(props.match)}`}</li>
<li>{`location prop -> ${JSON.stringify(props.location)}`}</li>
</ul>
);
const Nav = withRouter(Navigation);
const Topic = ({ match, location }) => (
<div>
<h3>{match.params.topicId}</h3>
<li>{`match prop -> ${JSON.stringify(match)}`}</li>
<li>{`location prop -> ${JSON.stringify(location)}`}</li>
</div>
);
const Topics = ({ match, location, history }) => (
<div>
<h2>Topics</h2>
<li>{`match prop -> ${JSON.stringify(match)}`}</li>
<li>{`location prop -> ${JSON.stringify(location)}`}</li>
<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>
);
ReactDOM.render(<BasicExample />, document.getElementById("root"));
<body>
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
</body>
For detecting :topicId in <Nav> use matchPath imported from react-router:
import { matchPath } from 'react-router'
matchPath(location.pathname, {
path:'/topics/:topicId',
exact: true,
strict: false
}})
This is a little outdated now. Can be achieved with react-router-dom by simply doing something like:
import { withRouter } from 'react-router-dom';
console.log(props.match.params);
Don't forget to wrap the component inside withRouter
export default withRouter(YourComponent);
props.match.params will return an object with all the params.

Resources