Passing props through NavLink - reactjs

I am trying to pass props from NavLink in react-router-dom to another component (component that router routes to). The code is:
<HashRouter>
<div className="navigation">
<NavLink to="/" className="navigation-link">Home</NavLink>
<NavLink quizid="60fcf0f3b55bab18d841e4c3" className="navigation-link"
to={{
pathname:'/thisquiz',
state: "60fcf0f3b55bab18d841e4c3"
}} exact>Quiz</NavLink>
</div>
<div>
<Route default exact path="/" component={Home}></Route>
<Route path="/thisquiz" component={Quiz}></Route>
</div>
</HashRouter>
In the Quiz component:
let location = useLocation();
console.log("The state is "+location.state);
The output shows The state is undefined. Please help.

Try
<NavLink quizid="60fcf0f3b55bab18d841e4c3" className="navigation-link"
to={{
pathname:'/thisquiz',
id: { id: "60fcf0f3b55bab18d841e4c3" }
}}
exact
>
Quiz
</NavLink>
then
const Quiz(props) => {
console.log(props.location.id);
return (
// code here
)
}
Also, would be good if you change your div tag on <div className="navigation"> for a <nav> tag.
https://medium.com/#bopaiahmd.mca/how-to-pass-props-using-link-and-navlink-in-react-router-v4-75dc1d9507b4

Related

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 5.1 - useLocation hook - determine past locations

According to React Router 5.1 documentation it should be possible to see "where the app is now, where you want it to go, or even where it was". In my app I need to see "where it was" - what locations I have visited before landing at a specific location.
More precisely; I wish to find a prevoious location matching a certain pattern. That location might be two or three locations ago.
However - I cannot figure out how to perform this.
What is the best and recommended approach to achieve this?
Kind regards /K
I turns out the best way, for me, to see where the application has been been is to simply use the state property in the React Router Link.
This article on how to pass state from Link to components really helped explain how to use the Link state.
Basically the Link can pass the state property to the rendered component.
<Link to={{ pathname: "/courses", state: { fromDashboard: true } }} />
The rendered component then access the state via props.location.state
This in conjunction with passing props to components generating the links solved my problem! (^__^)
In React Router you can use the goBack() method if you need to react a previous path in your history. Also, there is a possibility to push your path to history state, but that’s only needed if you need to know the URL of the previous location.
You can read more about this functionality from here: https://reacttraining.com/react-router/web/api/history
You can check this example. Hope it helps you.
import React from "react";
import {BrowserRouter as Router,Switch,Route,Link} from "react-router-dom";
import { withRouter } from "react-router";
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
const About = withRouter(({history, ...props}) => (
<h1 {...props}>
About
<hr/>
<button onClick={() => {history.push('/')}}>go back</button>
</h1>
));
Another Example for going back -2
import React from "react";
import {BrowserRouter as Router, Switch, Route, Link} from "react-router-dom";
import {withRouter} from "react-router";
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/about/insideabout">Inside About</Link>
</li>
</ul>
<hr/>
<Switch>
<Route exact path="/">
<Home/>
</Route>
<Route exact path="/about">
<About/>
</Route>
<Route path="/about/insideabout">
<InsideAbout/>
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
return (
<div>
<h2>Home</h2>
</div>
);
}
const About = withRouter(({history, ...props}) => (
<div>
<h1>
About
<hr/>
<button onClick={() => {
// history.push('/')
history.goBack(-1);
}}>go back
</button>
</h1>
</div>
));
const InsideAbout = withRouter(({history, ...props}) => (
<h1 {...props}>
Inside About
<hr/>
<button onClick={() => {
history.goBack();
}}>go back
</button>
<button onClick={() => {
history.go(-2);
}}>go home
</button>
</h1>
));

Login page in react using react hooks

I tried to do sample login page in react using react hooks(useState()).
When user is not login I redirect it to home. When I use hooks, that is not being redirected.
here is my code
import React, {useState} from 'react';
import {BrowserRouter as Router, Route, NavLink, Redirect} from 'react-router-dom';
import './App.css';
function App() {
const [isLogin, changeLogin] = useState(false);
return (
<Router>
<div>
<ul>
<li>
<NavLink to="/" exact activeStyle={{'color':'green'}}>Home</NavLink>
</li>
<li>
<NavLink to="/about" activeStyle={{'color':'green'}}>About</NavLink>
</li>
<li>
<NavLink to="/user/stack" activeStyle={{'color':'green'}}>User Stack</NavLink>
</li>
<li>
<NavLink to="/user/overflow" activeStyle={{'color':'green'}}>User Overflow</NavLink>
</li>
</ul>
<input className="loginButton" type="button" value={isLogin ? 'Logout' : 'Login'} onClick={ () => changeLogin(!isLogin)}/>
<hr/>
<Route path="/" exact strict render={() => <Home/>} />
<Route path="/about" exact strict render={() => <About/>} />
<Route path="/user/:username" exact strict render={({match}) => {
return(isLogin ? (<User username={match.params.username}/>) : (<Redirect to="/" />))
}} component={User} />
</div>
</Router>
);
}
function Home(){
return(
<div>
<h2>Home</h2>
</div>
)
}
function About(){
return(
<div>
<h2>About</h2>
</div>
)
}
const User = ({match}) => {
return(<h2>Welcome User {match.params.username}</h2>);
}
export default App;
I use usestate to handle the state which tells the user is logged in or not. How can I achieve this using react hooks?
Removing component={User} on your route should solve the problem :
<Route path="/user/:username" exact strict
render={({match}) => isLogin ?
<User username={match.params.username}/> :
<Redirect to="/" />
}
/>

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.

ReactJS - Unknown prop `activeClassName` on <a> tag. Remove this prop from the element

I am using react 15.4.2 and react-router4.0.0 and This project was bootstrapped with Create React App.
Here is my Code.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import {BrowserRouter as Router,Route,Link} from 'react-router-dom'
const AboutPage = () => {
return(
<section>
<h2>This is About page</h2>
<Link activeClassName="active" to="/about/nestedone">Nestedone</Link>
{' '}
<Link activeClassName="active" to="/about/nestedtwo">Nested two</Link>
</section>
)
}
const HomePage = () => {
return(
<section>
<h2>This is Home page</h2>
<Link to="/about">About</Link>
</section>
)
}
const NestedOne = () => {
return (
<section>
<h2>Nested page 1</h2>
</section>
)
}
const NestedTwo = () => {
return (
<section>
<h2>Nested page 2</h2>
</section>
)
}
ReactDOM.render(
<Router>
<section>
<Route exact path="/" component={HomePage} />
<Route path="/about" component={AboutPage} />
<Route path="/about/nestedone" component={NestedOne} />
<Route path="/about/nestedtwo" component={NestedTwo} />
</section>
</Router>,
document.getElementById('root')
);
When I browse /about, I am getting this error:
"Warning: Unknown prop activeClassName on tag. Remove this prop from the element.
What am I doing wrong here?
Thanks!
From v6 onwards of react-router replace activeClassName with className and use navData.isActive to toggle style.
Old way:
<NavLink to="/home" activeClassName="active-style">Home</NavLink>
v6 onwards:
<NavLink to="/home" className={(navData) => (navData.isActive ? "active-style" : 'none')}>Home</NavLink>
or you can also destructure isActive from navData:
<NavLink to="/home" className={({isActive}) => (isActive ? "active-style" : 'none')}>Home</NavLink>
The activeClassName property is not a property of Link but of NavLink.
So, just change your code to use NavLink instead of Link:
const AboutPage = () => {
return(
<section>
<h2>This is About page</h2>
<NavLink activeClassName="active" to="/about/nestedone">Nestedone</NavLink>
{' '}
<NavLink activeClassName="active" to="/about/nestedtwo">Nested two</NavLink>
</section>
)
Remember to import the NavLink from the react-router-dom:
import { NavLink } from 'react-router-dom'
In React Router v6 activeClassName has been changed to just className where a prop isActive can be used to manipulate the styling.
For more info
https://reactrouter.com/docs/en/v6/upgrading/v5#remove-activeclassname-and-activestyle-props-from-navlink-
const AboutPage = () => {
return(
<section>
<h2>This is About page</h2>
<NavLink className={({ isActive }) => isActive? "active": ''} to="/about/nestedone">Nestedone</NavLink>
{' '}
<NavLink className={({ isActive }) => isActive? "active": ''} to="/about/nestedtwo">Nested two</NavLink>
</section>
)
}
activeClassName is not a property of Link but of NavLink.
Since react-router v4 beta8, the property is active by default. Verify which version is installed in your node modules folder
My issue was that I was using reactstrap and importing their NavLink element which does NOT have the attribute activeClassName. Make sure you import NavLink from the react-router library like this:
import { NavLink } from 'react-router-dom'
Using activeclassname instead of activeClassName fixed it for me, like so:
<NavLink to="/home" activeclassname="active">Home</NavLink>

Resources