How to Route To a Different Page in Reactjs After Registration [duplicate] - javascript

With react-router I can use the Link element to create links which are natively handled by react router.
I see internally it calls this.context.transitionTo(...).
I want to do a navigation. Not from a link, but from a dropdown selection (as an example). How can I do this in code? What is this.context?
I saw the Navigation mixin, but can I do this without mixins?

UPDATE: 2022: React Router v6.6.1 with useNavigate
The useHistory() hook is now deprecated. If you are using React Router 6, the proper way to navigate programmatically is as follows:
import { useNavigate } from "react-router-dom";
function HomeButton() {
const navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
React Router v5.1.0 with hooks
There is a new useHistory hook in React Router >5.1.0 if you are using React >16.8.0 and functional components.
import { useHistory } from "react-router-dom";
function HomeButton() {
const history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
React Router v4
With v4 of React Router, there are three approaches that you can take to programmatic routing within components.
Use the withRouter higher-order component.
Use composition and render a <Route>
Use the context.
React Router is mostly a wrapper around the history library. history handles interaction with the browser's window.history for you with its browser and hash histories. It also provides a memory history which is useful for environments that don't have a global history. This is particularly useful in mobile app development (react-native) and unit testing with Node.
A history instance has two methods for navigating: push and replace. If you think of the history as an array of visited locations, push will add a new location to the array and replace will replace the current location in the array with the new one. Typically you will want to use the push method when you are navigating.
In earlier versions of React Router, you had to create your own history instance, but in v4 the <BrowserRouter>, <HashRouter>, and <MemoryRouter> components will create a browser, hash, and memory instances for you. React Router makes the properties and methods of the history instance associated with your router available through the context, under the router object.
1. Use the withRouter higher-order component
The withRouter higher-order component will inject the history object as a prop of the component. This allows you to access the push and replace methods without having to deal with the context.
import { withRouter } from 'react-router-dom'
// this also works with react-router-native
const Button = withRouter(({ history }) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
Click Me!
</button>
))
2. Use composition and render a <Route>
The <Route> component isn't just for matching locations. You can render a pathless route and it will always match the current location. The <Route> component passes the same props as withRouter, so you will be able to access the history methods through the history prop.
import { Route } from 'react-router-dom'
const Button = () => (
<Route render={({ history}) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
Click Me!
</button>
)} />
)
3. Use the context*
But you probably should not
The last option is one that you should only use if you feel comfortable working with React's context model (React's Context API is stable as of v16).
const Button = (props, context) => (
<button
type='button'
onClick={() => {
// context.history.push === history.push
context.history.push('/new-location')
}}
>
Click Me!
</button>
)
// you need to specify the context type so that it
// is available within the component
Button.contextTypes = {
history: React.PropTypes.shape({
push: React.PropTypes.func.isRequired
})
}
1 and 2 are the simplest choices to implement, so for most use cases, they are your best bets.

React-Router v6+ Answer
TL;DR: You can use the new useNavigate hook.
import { useNavigate } from "react-router-dom";
function Component() {
let navigate = useNavigate();
// Somewhere in your code, e.g. inside a handler:
navigate("/posts");
}
The useNavigate hook returns a function which can be used for programmatic navigation.
Example from the react router documentaion
import { useNavigate } from "react-router-dom";
function SignupForm() {
let navigate = useNavigate();
async function handleSubmit(event) {
event.preventDefault();
await submitForm(event.target);
navigate("../success", { replace: true });
// replace: true will replace the current entry in
// the history stack instead of adding a new one.
}
return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}
React-Router 5.1.0+ Answer (using hooks and React >16.8)
You can use the useHistory hook on Functional Components and Programmatically navigate:
import { useHistory } from "react-router-dom";
function HomeButton() {
let history = useHistory();
// use history.push('/some/path') here
};
React-Router 4.0.0+ Answer
In 4.0 and above, use the history as a prop of your component.
class Example extends React.Component {
// use `this.props.history.push('/some/path')` here
};
NOTE: this.props.history does not exist in the case your component was not rendered by <Route>. You should use <Route path="..." component={YourComponent}/> to have this.props.history in YourComponent
React-Router 3.0.0+ Answer
In 3.0 and above, use the router as a prop of your component.
class Example extends React.Component {
// use `this.props.router.push('/some/path')` here
};
React-Router 2.4.0+ Answer
In 2.4 and above, use a higher order component to get the router as a prop of your component.
import { withRouter } from 'react-router';
class Example extends React.Component {
// use `this.props.router.push('/some/path')` here
};
// Export the decorated class
var DecoratedExample = withRouter(Example);
// PropTypes
Example.propTypes = {
router: React.PropTypes.shape({
push: React.PropTypes.func.isRequired
}).isRequired
};
React-Router 2.0.0+ Answer
This version is backwards compatible with 1.x so there's no need to an Upgrade Guide. Just going through the examples should be good enough.
That said, if you wish to switch to the new pattern, there's a browserHistory module inside the router that you can access with
import { browserHistory } from 'react-router'
Now you have access to your browser history, so you can do things like push, replace, etc... Like:
browserHistory.push('/some/path')
Further reading:
Histories and
Navigation
React-Router 1.x.x Answer
I will not go into upgrading details. You can read about that in the Upgrade Guide
The main change about the question here is the change from Navigation mixin to History. Now it's using the browser historyAPI to change route so we will use pushState() from now on.
Here's an exemple using Mixin:
var Example = React.createClass({
mixins: [ History ],
navigateToHelpPage () {
this.history.pushState(null, `/help`);
}
})
Note that this History comes from rackt/history project. Not from React-Router itself.
If you don't want to use Mixin for some reason (maybe because of ES6 class), then you can access the history that you get from the router from this.props.history. It will be only accessible for the components rendered by your Router. So, if you want to use it in any child components it needs to be passed down as an attribute via props.
You can read more about the new release at their 1.0.x documentation
Here is a help page specifically about navigating outside your component
It recommends grabbing a reference history = createHistory() and calling replaceState on that.
React-Router 0.13.x Answer
I got into the same problem and could only find the solution with the Navigation mixin that comes with react-router.
Here's how I did it
import React from 'react';
import {Navigation} from 'react-router';
let Authentication = React.createClass({
mixins: [Navigation],
handleClick(e) {
e.preventDefault();
this.transitionTo('/');
},
render(){
return (<div onClick={this.handleClick}>Click me!</div>);
}
});
I was able to call transitionTo() without the need to access .context
Or you could try the fancy ES6 class
import React from 'react';
export default class Authentication extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
e.preventDefault();
this.context.router.transitionTo('/');
}
render(){
return (<div onClick={this.handleClick}>Click me!</div>);
}
}
Authentication.contextTypes = {
router: React.PropTypes.func.isRequired
};
React-Router-Redux
Note: if you're using Redux, there is another project called
React-Router-Redux that gives you
redux bindings for ReactRouter, using somewhat the same approach that
React-Redux does
React-Router-Redux has a few methods available that allow for simple navigating from inside action creators. These can be particularly useful for people that have existing architecture in React Native, and they wish to utilize the same patterns in React Web with minimal boilerplate overhead.
Explore the following methods:
push(location)
replace(location)
go(number)
goBack()
goForward()
Here is an example usage, with Redux-Thunk:
./actioncreators.js
import { goBack } from 'react-router-redux'
export const onBackPress = () => (dispatch) => dispatch(goBack())
./viewcomponent.js
<button
disabled={submitting}
className="cancel_button"
onClick={(e) => {
e.preventDefault()
this.props.onBackPress()
}}
>
CANCEL
</button>

React-Router v2
For the most recent release (v2.0.0-rc5), the recommended navigation method is by directly pushing onto the history singleton. You can see that in action in the Navigating outside of Components doc.
Relevant excerpt:
import { browserHistory } from 'react-router';
browserHistory.push('/some/path');
If using the newer react-router API, you need to make use of the history from this.props when inside of components so:
this.props.history.push('/some/path');
It also offers pushState but that is deprecated per logged warnings.
If using react-router-redux, it offers a push function you can dispatch like so:
import { push } from 'react-router-redux';
this.props.dispatch(push('/some/path'));
However this may be only used to change the URL, not to actually navigate to the page.

React-Router 4.x answer
On my end, I like to have a single history object that I can carry even outside components. I like to have a single history.js file that I import on demand, and just manipulate it.
You just have to change BrowserRouter to Router, and specify the history prop. This doesn't change anything for you, except that you have your own history object that you can manipulate as you want.
You need to install history, the library used by react-router.
Example usage, ES6 notation:
history.js
import createBrowserHistory from 'history/createBrowserHistory'
export default createBrowserHistory()
BasicComponent.js
import React, { Component } from 'react';
import history from './history';
class BasicComponent extends Component {
goToIndex(e){
e.preventDefault();
history.push('/');
}
render(){
return <a href="#" onClick={this.goToIndex}>Previous</a>;
}
}
If you have to navigate from a component that is actually rendered from a Route component, you can also access history from props, like that:
BasicComponent.js
import React, { Component } from 'react';
class BasicComponent extends Component {
navigate(e){
e.preventDefault();
this.props.history.push('/url');
}
render(){
return <a href="#" onClick={this.navigate}>Previous</a>;
}
}

Here's how you do this with react-router v2.0.0 with ES6. react-router has moved away from mixins.
import React from 'react';
export default class MyComponent extends React.Component {
navigateToPage = () => {
this.context.router.push('/my-route')
};
render() {
return (
<button onClick={this.navigateToPage}>Go!</button>
);
}
}
MyComponent.contextTypes = {
router: React.PropTypes.object.isRequired
}

For this one, who does not control the server side and because of this is using hash router v2:
Place your history into separate file (e.g. app_history.js ES6):
import { useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false });
export default appHistory;
And use it everywhere!
Your entry point for react-router (app.js ES6):
import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Redirect } from 'react-router'
import appHistory from './app_history'
...
const render((
<Router history={appHistory}>
...
</Router>
), document.querySelector('[data-role="app"]'));
Your navigation inside any component (ES6):
import appHistory from '../app_history'
...
ajaxLogin('/login', (err, data) => {
if (err) {
console.error(err); // login failed
} else {
// logged in
appHistory.replace('/dashboard'); // or .push() if you don't need .replace()
}
})

React Router v6
I haven't touched React in a while, but want to thank and highlight the comment below by Shimrit Snapir:
on React-Router 6.0 <Redirect /> changed to <Navigate />
React Router V4
tl:dr;
if (navigate) {
return <Redirect to="/" push={true} />
}
The simple and declarative answer is that you need to use <Redirect to={URL} push={boolean} /> in combination with setState()
push: boolean - when true, redirecting will push a new entry onto the history instead of replacing the current one.
import { Redirect } from 'react-router'
class FooBar extends React.Component {
state = {
navigate: false
}
render() {
const { navigate } = this.state
// Here is the important part
if (navigate) {
return <Redirect to="/" push={true} />
}
// ^^^^^^^^^^^^^^^^^^^^^^^
return (
<div>
<button onClick={() => this.setState({ navigate: true })}>
Home
</button>
</div>
)
}
}
A full example is here. Read more here.
PS. The example uses ES7+ Property Initializers to initialise state. Look here as well, if you're interested.

Warning: this answer covers only ReactRouter versions before 1.0
I will update this answer with 1.0.0-rc1 use cases after!
You can do this without mixins too.
let Authentication = React.createClass({
contextTypes: {
router: React.PropTypes.func
},
handleClick(e) {
e.preventDefault();
this.context.router.transitionTo('/');
},
render(){
return (<div onClick={this.handleClick}>Click me!</div>);
}
});
The gotcha with contexts is that it is not accessible unless you define the contextTypes on the class.
As for what is context, it is an object, like props, that are passed down from parent to child, but it is passed down implicitly, without having to redeclare props each time. See https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html

Here's the simplest and cleanest way to do it, circa current React-Router 3.0.0 and ES6:
React-Router 3.x.x with ES6:
import { withRouter } from 'react-router';
class Example extends React.Component {
// use `this.props.router.push('/some/path')` here
};
// Export the decorated class
export default withRouter(Example);
Or, if it's not your default class, export like:
withRouter(Example);
export { Example };
Note that in 3.x.x, the <Link> component itself is using router.push, so you can pass it anything you would pass the <Link to= tag, like:
this.props.router.push({pathname: '/some/path', query: {key1: 'val1', key2: 'val2'})'

To do the navigation programmatically, you need to push a new history to the props.history in your component, so something like this can do the work for you:
//using ES6
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick(e) {
e.preventDefault()
/* Look at here, you can add it here */
this.props.history.push('/redirected');
}
render() {
return (
<div>
<button onClick={this.handleClick}>
Redirect!!!
</button>
</div>
)
}
}
export default App;

For ES6 + React components, the following solution worked for me.
I followed Felippe skinner, but added an end to end solution to help beginners like me.
Below are the versions I used:
"react-router": "^2.7.0"
"react": "^15.3.1"
Below is my react component where I used programmatic navigation using react-router:
import React from 'react';
class loginComp extends React.Component {
constructor( context) {
super(context);
this.state = {
uname: '',
pwd: ''
};
}
redirectToMainPage(){
this.context.router.replace('/home');
}
render(){
return <div>
// skipping html code
<button onClick={this.redirectToMainPage.bind(this)}>Redirect</button>
</div>;
}
};
loginComp.contextTypes = {
router: React.PropTypes.object.isRequired
}
module.exports = loginComp;
Below is the configuration for my router:
import { Router, Route, IndexRedirect, browserHistory } from 'react-router'
render(<Router history={browserHistory}>
<Route path='/' component={ParentComp}>
<IndexRedirect to = "/login"/>
<Route path='/login' component={LoginComp}/>
<Route path='/home' component={HomeComp}/>
<Route path='/repair' component={RepairJobComp} />
<Route path='/service' component={ServiceJobComp} />
</Route>
</Router>, document.getElementById('root'));

It may not be the best approach but... Using react-router v4, the following TypeScript code could give an idea for some.
In the rendered component below, e.g. LoginPage, router object is accessible and just call router.transitionTo('/homepage') to navigate.
Navigation code was taken from.
"react-router": "^4.0.0-2",
"react": "^15.3.1",
import Router from 'react-router/BrowserRouter';
import { History } from 'react-history/BrowserHistory';
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
interface MatchWithPropsInterface {
component: typeof React.Component,
router: Router,
history: History,
exactly?: any,
pattern: string
}
class MatchWithProps extends React.Component<MatchWithPropsInterface,any> {
render() {
return(
<Match {...this.props} render={(matchProps) => (
React.createElement(this.props.component, this.props)
)}
/>
)
}
}
ReactDOM.render(
<Router>
{({ router }) => (
<div>
<MatchWithProps exactly pattern="/" component={LoginPage} router={router} history={history} />
<MatchWithProps pattern="/login" component={LoginPage} router={router} history={history} />
<MatchWithProps pattern="/homepage" component={HomePage} router={router} history={history} />
<Miss component={NotFoundView} />
</div>
)}
</Router>,
document.getElementById('app')
);

In React Router v4, I follow these two ways to route programmatically.
this.props.history.push("/something/something")
this.props.history.replace("/something/something")
Number two
Replaces the current entry on the history stack
To get history in props you may have to wrap your component with
withRouter
In React Router v6
import { useNavigate } from "react-router-dom";
function Invoices() {
let navigate = useNavigate();
return (
<div>
<NewInvoiceForm
onSubmit={async event => {
let newInvoice = await createInvoice(event.target);
navigate(`/invoices/${newInvoice.id}`);
}}
/>
</div>
);
}
Getting Started with React Router v6

In React-Router v4 and ES6
You can use withRouter and this.props.history.push.
import {withRouter} from 'react-router-dom';
class Home extends Component {
componentDidMount() {
this.props.history.push('/redirect-to');
}
}
export default withRouter(Home);

To use withRouter with a class-based component, try something like this below.
Don't forget to change the export statement to use withRouter:
import { withRouter } from 'react-router-dom'
class YourClass extends React.Component {
yourFunction = () => {
doSomeAsyncAction(() =>
this.props.history.push('/other_location')
)
}
render() {
return (
<div>
<Form onSubmit={ this.yourFunction } />
</div>
)
}
}
export default withRouter(YourClass);

With React-Router v4 on the horizon, there is now a new way of doing this.
import { MemoryRouter, BrowserRouter } from 'react-router';
const navigator = global && global.navigator && global.navigator.userAgent;
const hasWindow = typeof window !== 'undefined';
const isBrowser = typeof navigator !== 'undefined' && navigator.indexOf('Node.js') === -1;
const Router = isBrowser ? BrowserRouter : MemoryRouter;
<Router location="/page-to-go-to"/>
react-lego is an example app that shows how to use/update react-router and it includes example functional tests which navigate the app.

Based on the previous answers from José Antonio Postigo and Ben Wheeler:
The novelty? Is to be written in TypeScript and uses decorators
or a static property/field
import * as React from "react";
import Component = React.Component;
import { withRouter } from "react-router";
export interface INavigatorProps {
router?: ReactRouter.History.History;
}
/**
* Note: goes great with mobx
* #inject("something") #withRouter #observer
*/
#withRouter
export class Navigator extends Component<INavigatorProps, {}>{
navigate: (to: string) => void;
constructor(props: INavigatorProps) {
super(props);
let self = this;
this.navigate = (to) => self.props.router.push(to);
}
render() {
return (
<ul>
<li onClick={() => this.navigate("/home")}>
Home
</li>
<li onClick={() => this.navigate("/about")}>
About
</li>
</ul>
)
}
}
/**
* Non decorated
*/
export class Navigator2 extends Component<INavigatorProps, {}> {
static contextTypes = {
router: React.PropTypes.object.isRequired,
};
navigate: (to: string) => void;
constructor(props: INavigatorProps, context: any) {
super(props, context);
let s = this;
this.navigate = (to) =>
s.context.router.push(to);
}
render() {
return (
<ul>
<li onClick={() => this.navigate("/home")}>
Home
</li>
<li onClick={() => this.navigate("/about")}>
About
</li>
</ul>
)
}
}
with whatever npm installed today.
"react-router": "^3.0.0" and
"#types/react-router": "^2.0.41"

For those who are already using React Router v6, this can be done using useNavigate hook provided by react-router.
Navigation with this hook is pretty simple:
import { generatePath, useNavigate } from 'react-router';
navigate(-1); // navigates back
navigate('/my/path'); // navigates to a specific path
navigate(generatePath('my/path/:id', { id: 1 })); // navigates to a dynamic path, generatePath is very useful for url replacements

For Latest react-router-dom v6
useHistory() is replaced with useNavigate().
You need to use:
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
navigate('/your-page-link');

With the current React version (15.3), this.props.history.push('/location'); worked for me, but it showed the following warning:
browser.js:49 Warning: [react-router] props.history and
context.history are deprecated. Please use context.router.
and I solved it using context.router like this:
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.backPressed = this.backPressed.bind(this);
}
backPressed() {
this.context.router.push('/back-location');
}
...
}
MyComponent.contextTypes = {
router: React.PropTypes.object.isRequired
};
export default MyComponent;

React Router v6 with hooks
import {useNavigate} from 'react-router-dom';
let navigate = useNavigate();
navigate('home');
And to move across the browser history,
navigate(-1); ---> Go back
navigate(1); ---> Go forward
navigate(-2); ---> Move two steps backward.

If you are using hash or browser history then you can do
hashHistory.push('/login');
browserHistory.push('/login');

React-Router V4
If you're using version 4 then you can use my library (shameless plug) where you simply dispatch an action and everything just works!
dispatch(navigateTo("/aboutUs"));
trippler

Those who are facing issues in implementing this in React Router v4.
Here is a working solution for navigating through the React app from redux actions.
File history.js
import createHistory from 'history/createBrowserHistory'
export default createHistory()
Files App.js/Route.jsx
import { Router, Route } from 'react-router-dom'
import history from './history'
...
<Router history={history}>
<Route path="/test" component={Test}/>
</Router>
File *another_file.js or redux file
import history from './history'
history.push('/test') // This should change the URL and rerender Test component
All thanks to this comment on GitHub:
ReactTraining issues comment

You can also use the useHistory hook in a stateless component. Example from the documentation:
import { useHistory } from "react-router"
function HomeButton() {
const history = useHistory()
return (
<button type="button" onClick={() => history.push("/home")}>
Go home
</button>
)
}
Note: Hooks were added in react-router#5.1.0 and require react#>=16.8

Programmatically navigate in class-based components.
import { Redirect } from "react-router-dom";
class MyComponent extends React.Component{
state = {rpath: null}
const goTo = (path) => this.setState({rpath: path});
render(){
if(this.state.rpath){
return <Redirect to={this.state.rpath}/>
}
.....
.....
}
}

In my answer there are three different ways to redirect programmatically to a route. Some of the solutions has been presented already, but the following ones focused only for functional components with an additional demo application.
Using the following versions:
react: 16.13.1
react-dom: 16.13.1
react-router: 5.2.0
react-router-dom: 5.2.0
typescript: 3.7.2
Configuration:
So first of all the solution is using HashRouter, configured as follows:
<HashRouter>
// ... buttons for redirect
<Switch>
<Route exact path="/(|home)" children={Home} />
<Route exact path="/usehistory" children={UseHistoryResult} />
<Route exact path="/withrouter" children={WithRouterResult} />
<Route exact path="/redirectpush" children={RedirectPushResult} />
<Route children={Home} />
</Switch>
</HashRouter>
From the documentation about <HashRouter>:
A <Router> that uses the hash portion of the URL (i.e. window.location.hash) to keep your UI in sync with the URL.
Solutions:
Using <Redirect> to push using useState:
Using in a functional component (RedirectPushAction component from my repository) we can use useState to handle redirect. The tricky part is once the redirection happened, we need to set the redirect state back to false. By using setTimeOut with 0 delay we are waiting until React commits Redirect to the DOM and then getting back the button in order to use it the next time.
Please find my example below:
const [redirect, setRedirect] = useState(false);
const handleRedirect = useCallback(() => {
let render = null;
if (redirect) {
render = <Redirect to="/redirectpush" push={true} />
// In order wait until committing to the DOM
// and get back the button for clicking next time
setTimeout(() => setRedirect(false), 0);
}
return render;
}, [redirect]);
return <>
{handleRedirect()}
<button onClick={() => setRedirect(true)}>
Redirect push
</button>
</>
From <Redirect> documentation:
Rendering a <Redirect> will navigate to a new location. The new location will override the current location in the history stack, like server-side redirects (HTTP 3xx) do.
Using useHistory hook:
In my solution there is a component called UseHistoryAction which represents the following:
let history = useHistory();
return <button onClick={() => { history.push('/usehistory') }}>
useHistory redirect
</button>
The useHistory hook gives us access to the history object which helps us programmatically navigate or change routes.
Using withRouter, get the history from props:
Created one component called WithRouterAction, displays as below:
const WithRouterAction = (props:any) => {
const { history } = props;
return <button onClick={() => { history.push('/withrouter') }}>
withRouter redirect
</button>
}
export default withRouter(WithRouterAction);
Reading from withRouter documentation:
You can get access to the history object's properties and the closest <Route>'s match via the withRouter higher-order component. withRouter will pass updated match, location, and history props to the wrapped component whenever it renders.
Demo:
For better representation I have built a GitHub repository with these examples, please find it below:
React Router Programmatically Redirect Examples

The right answer was for me at the time of writing
this.context.router.history.push('/');
But you need to add PropTypes to your component
Header.contextTypes = {
router: PropTypes.object.isRequired
}
export default Header;
Don't forget to import PropTypes
import PropTypes from 'prop-types';

Maybe not the best solution, but it gets the job done:
import { Link } from 'react-router-dom';
// Create functional component Post
export default Post = () => (
<div className="component post">
<button className="button delete-post" onClick={() => {
// ... delete post
// then redirect, without page reload, by triggering a hidden Link
document.querySelector('.trigger.go-home').click();
}}>Delete Post</button>
<Link to="/" className="trigger go-home hidden"></Link>
</div>
);
Basically, logic tied to one action (in this case a post deletion) will end up calling a trigger for redirect. This is not ideal, because you will add a DOM node 'trigger' to your markup just so you can conveniently call it when needed. Also, you will directly interact with the DOM, which in a React component may not be desired.
Still, this type of redirect is not required that often. So one or two extra, hidden links in your component markup would not hurt that much, especially if you give them meaningful names.

If you happen to pair RR4 with redux through react-router-redux, using the routing action creators from react-router-redux is an option as well.
import { push, replace, ... } from 'react-router-redux'
class WrappedComponent extends React.Component {
handleRedirect(url, replaceState = true) {
replaceState
? this.props.dispatch(replace(url))
: this.props.dispatch(push(url))
}
render() { ... }
}
export default connect(null)(WrappedComponent)
If you use redux thunk/saga to manage async flow, import the above action creators in redux actions and hook to React components using mapDispatchToProps might be better.

Related

React Router & Global Context

I'm building an e-commerce website with React (my first ever React project) and I'm using React router to manage my pages.
I've got the following component tree structure:
<Router>
<BrowserRouter>
<Router>
<withRouter(Base)>
<Route>
<Base>
<BaseProvider>
<Context.Provider>
<Header>
<PageContent>
The standard React Router structure basically, and withRouter I've got the following:
Base.js
import React, { Component } from 'react';
import { withRouter } from 'react-router';
import { Header } from './Header';
import { Footer } from './Footer';
import Provider from '../../BaseProvider';
class Base extends Component {
render() {
return (
<Provider>
<Header/>
<div className="container">{this.props.children}</div>
<Footer />
</Provider>
);
}
}
BaseProvider.js
import React, { Component, createContext } from 'react';
const Context = createContext();
const { Provider, Consumer } = Context;
class BaseProvider extends Component {
state = {
cart: [],
basketTotal: 0,
priceTotal: 0,
};
addProductToCart = product => {
const cart = { ...this.state.cart };
cart[product.id] = product;
this.setState({ cart, basketTotal: Object.keys(cart).length });
};
render() {
return (
<Provider
value={{ state: this.state, addProductToCart: this.addProductToCart }}
>
{this.props.children}
</Provider>
);
}
}
export { Consumer };
export default BaseProvider;
This gives me a template essentially, so I just the children pages without having to include Header and Footer each time.
If I want to use my global context I'm having to import it each time, and it seems like I've done something wrong as surely I should be able to use this on any page since it's exported in BaseProvider?
If I was to visit the About page, I'd get the same component structure, but no access to the consumer without using:
import { Consumer } from '../../BaseProvider';
Why do I have to do this for each file even though it's exported and at the top level of my BaseProvider? It just seems such a bad pattern that I'd have to import it into about 20 files...
Without importing it, I just get:
Line 67: 'Consumer' is not defined no-undef
I tried just adding the contextType to base but I get: Warning: withRouter(Base): Function components do not support contextType.
Base.contextType = Consumer;
I feel like I've just implemented this wrong as surely this pattern should work a lot better.
I'd recommend using a Higher Order Component - a component that wraps other components with additional state or functionality.
const CartConsumer = Component => {
return class extends React.Component {
render() {
return (
<MyContext.Consumer>
<Component />
</MyContext.Consumer>
)
}
}
}
Then in any component where you'd like to use it, simply wrap in the export statement:
export default CartConsumer(ComponentWithContext)
This does not avoid importing completely, but it's far more minimal than using the consumer directly.

How to pass a default params or state to the react router? any alternative approach?

I have a state that is list of valid name (valid_list = []). I want to route to home page if someone types a /topics/:name which is not valid. I want o hence pass the valid list to the new component.
I have mentioned all my attempts at the end.
My parent component is this:
import React, { Component } from 'react'
import { BrowserRouter as Router, Route } from 'react-router-dom'
import Topic from '../topic'
class App extends Component { {
return (
<Router>
<div>
<Route path="/topics/:name" component={Topic} />
</div>
</Router>
);
}
the calling component is
import React,{ Component } from 'react'
import { Link } from 'react-router-dom'
class Maintopic extends Component { {
render() {
return (
<div>
<Link to='/topic:$name' > NextTopic </Link>
</div>
);
}
and called component is something where I am facing issues.
render() {
return (
<div>
<h1>{this.props.match.params.name}</h1>
</div>
);
}
Attempt 1:
Passing object to link:
<Link to={
{
pathname:`/topic/${name}`,
state:{
valid_list : this.state.valid_list
}
}}>
And then performing check on the called component by using artlist this.props.location.state.valid_list,
Here the problem is if I manually type URL like "/topic/name1" or "/topic/name2" i get error like "TypeError: Cannot read property 'valid_list' of undefined"
Attempt 2:
Having called and calling component both have manually stored state in their component
Here issues is I really want my valid_list to be dynamic and can be increased visa input button in future implementation. Plus it does not make sense to maintain different copies of same data.
If your valid_list is an array of stuff, one solution for your problem is making a decorator and decorates your component, so you should do it like this.
const withValidList = (valid_list) => (Component) => (routerProps) => {
return <Component {...routerProps} valid_list={valid_list} />
}
then in your <Route> you must decorate your component with the valid_list like this
import React, { Component } from 'react'
import { BrowserRouter as Router, Route } from 'react-router-dom'
import Topic from '../topic'
class App extends Component { {
return (
<Router>
<div>
<Route path="/topics/:name" component={withValidList(['list1', 'list2'])(Topic)} />
</div>
</Router>
);
}
that's should works, so basically your are wrapping your component adding some extra functionality with the decorator pattern, that give you the ability to passing down the valid_list props to be used into the Topic component.

Cannot find reason: React: Redirects onclick does not work but other redirects work(Link works and history.push also works) [duplicate]

With react-router I can use the Link element to create links which are natively handled by react router.
I see internally it calls this.context.transitionTo(...).
I want to do a navigation. Not from a link, but from a dropdown selection (as an example). How can I do this in code? What is this.context?
I saw the Navigation mixin, but can I do this without mixins?
UPDATE: 2022: React Router v6.6.1 with useNavigate
The useHistory() hook is now deprecated. If you are using React Router 6, the proper way to navigate programmatically is as follows:
import { useNavigate } from "react-router-dom";
function HomeButton() {
const navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
React Router v5.1.0 with hooks
There is a new useHistory hook in React Router >5.1.0 if you are using React >16.8.0 and functional components.
import { useHistory } from "react-router-dom";
function HomeButton() {
const history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
React Router v4
With v4 of React Router, there are three approaches that you can take to programmatic routing within components.
Use the withRouter higher-order component.
Use composition and render a <Route>
Use the context.
React Router is mostly a wrapper around the history library. history handles interaction with the browser's window.history for you with its browser and hash histories. It also provides a memory history which is useful for environments that don't have a global history. This is particularly useful in mobile app development (react-native) and unit testing with Node.
A history instance has two methods for navigating: push and replace. If you think of the history as an array of visited locations, push will add a new location to the array and replace will replace the current location in the array with the new one. Typically you will want to use the push method when you are navigating.
In earlier versions of React Router, you had to create your own history instance, but in v4 the <BrowserRouter>, <HashRouter>, and <MemoryRouter> components will create a browser, hash, and memory instances for you. React Router makes the properties and methods of the history instance associated with your router available through the context, under the router object.
1. Use the withRouter higher-order component
The withRouter higher-order component will inject the history object as a prop of the component. This allows you to access the push and replace methods without having to deal with the context.
import { withRouter } from 'react-router-dom'
// this also works with react-router-native
const Button = withRouter(({ history }) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
Click Me!
</button>
))
2. Use composition and render a <Route>
The <Route> component isn't just for matching locations. You can render a pathless route and it will always match the current location. The <Route> component passes the same props as withRouter, so you will be able to access the history methods through the history prop.
import { Route } from 'react-router-dom'
const Button = () => (
<Route render={({ history}) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
Click Me!
</button>
)} />
)
3. Use the context*
But you probably should not
The last option is one that you should only use if you feel comfortable working with React's context model (React's Context API is stable as of v16).
const Button = (props, context) => (
<button
type='button'
onClick={() => {
// context.history.push === history.push
context.history.push('/new-location')
}}
>
Click Me!
</button>
)
// you need to specify the context type so that it
// is available within the component
Button.contextTypes = {
history: React.PropTypes.shape({
push: React.PropTypes.func.isRequired
})
}
1 and 2 are the simplest choices to implement, so for most use cases, they are your best bets.
React-Router v6+ Answer
TL;DR: You can use the new useNavigate hook.
import { useNavigate } from "react-router-dom";
function Component() {
let navigate = useNavigate();
// Somewhere in your code, e.g. inside a handler:
navigate("/posts");
}
The useNavigate hook returns a function which can be used for programmatic navigation.
Example from the react router documentaion
import { useNavigate } from "react-router-dom";
function SignupForm() {
let navigate = useNavigate();
async function handleSubmit(event) {
event.preventDefault();
await submitForm(event.target);
navigate("../success", { replace: true });
// replace: true will replace the current entry in
// the history stack instead of adding a new one.
}
return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}
React-Router 5.1.0+ Answer (using hooks and React >16.8)
You can use the useHistory hook on Functional Components and Programmatically navigate:
import { useHistory } from "react-router-dom";
function HomeButton() {
let history = useHistory();
// use history.push('/some/path') here
};
React-Router 4.0.0+ Answer
In 4.0 and above, use the history as a prop of your component.
class Example extends React.Component {
// use `this.props.history.push('/some/path')` here
};
NOTE: this.props.history does not exist in the case your component was not rendered by <Route>. You should use <Route path="..." component={YourComponent}/> to have this.props.history in YourComponent
React-Router 3.0.0+ Answer
In 3.0 and above, use the router as a prop of your component.
class Example extends React.Component {
// use `this.props.router.push('/some/path')` here
};
React-Router 2.4.0+ Answer
In 2.4 and above, use a higher order component to get the router as a prop of your component.
import { withRouter } from 'react-router';
class Example extends React.Component {
// use `this.props.router.push('/some/path')` here
};
// Export the decorated class
var DecoratedExample = withRouter(Example);
// PropTypes
Example.propTypes = {
router: React.PropTypes.shape({
push: React.PropTypes.func.isRequired
}).isRequired
};
React-Router 2.0.0+ Answer
This version is backwards compatible with 1.x so there's no need to an Upgrade Guide. Just going through the examples should be good enough.
That said, if you wish to switch to the new pattern, there's a browserHistory module inside the router that you can access with
import { browserHistory } from 'react-router'
Now you have access to your browser history, so you can do things like push, replace, etc... Like:
browserHistory.push('/some/path')
Further reading:
Histories and
Navigation
React-Router 1.x.x Answer
I will not go into upgrading details. You can read about that in the Upgrade Guide
The main change about the question here is the change from Navigation mixin to History. Now it's using the browser historyAPI to change route so we will use pushState() from now on.
Here's an exemple using Mixin:
var Example = React.createClass({
mixins: [ History ],
navigateToHelpPage () {
this.history.pushState(null, `/help`);
}
})
Note that this History comes from rackt/history project. Not from React-Router itself.
If you don't want to use Mixin for some reason (maybe because of ES6 class), then you can access the history that you get from the router from this.props.history. It will be only accessible for the components rendered by your Router. So, if you want to use it in any child components it needs to be passed down as an attribute via props.
You can read more about the new release at their 1.0.x documentation
Here is a help page specifically about navigating outside your component
It recommends grabbing a reference history = createHistory() and calling replaceState on that.
React-Router 0.13.x Answer
I got into the same problem and could only find the solution with the Navigation mixin that comes with react-router.
Here's how I did it
import React from 'react';
import {Navigation} from 'react-router';
let Authentication = React.createClass({
mixins: [Navigation],
handleClick(e) {
e.preventDefault();
this.transitionTo('/');
},
render(){
return (<div onClick={this.handleClick}>Click me!</div>);
}
});
I was able to call transitionTo() without the need to access .context
Or you could try the fancy ES6 class
import React from 'react';
export default class Authentication extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
e.preventDefault();
this.context.router.transitionTo('/');
}
render(){
return (<div onClick={this.handleClick}>Click me!</div>);
}
}
Authentication.contextTypes = {
router: React.PropTypes.func.isRequired
};
React-Router-Redux
Note: if you're using Redux, there is another project called
React-Router-Redux that gives you
redux bindings for ReactRouter, using somewhat the same approach that
React-Redux does
React-Router-Redux has a few methods available that allow for simple navigating from inside action creators. These can be particularly useful for people that have existing architecture in React Native, and they wish to utilize the same patterns in React Web with minimal boilerplate overhead.
Explore the following methods:
push(location)
replace(location)
go(number)
goBack()
goForward()
Here is an example usage, with Redux-Thunk:
./actioncreators.js
import { goBack } from 'react-router-redux'
export const onBackPress = () => (dispatch) => dispatch(goBack())
./viewcomponent.js
<button
disabled={submitting}
className="cancel_button"
onClick={(e) => {
e.preventDefault()
this.props.onBackPress()
}}
>
CANCEL
</button>
React-Router v2
For the most recent release (v2.0.0-rc5), the recommended navigation method is by directly pushing onto the history singleton. You can see that in action in the Navigating outside of Components doc.
Relevant excerpt:
import { browserHistory } from 'react-router';
browserHistory.push('/some/path');
If using the newer react-router API, you need to make use of the history from this.props when inside of components so:
this.props.history.push('/some/path');
It also offers pushState but that is deprecated per logged warnings.
If using react-router-redux, it offers a push function you can dispatch like so:
import { push } from 'react-router-redux';
this.props.dispatch(push('/some/path'));
However this may be only used to change the URL, not to actually navigate to the page.
React-Router 4.x answer
On my end, I like to have a single history object that I can carry even outside components. I like to have a single history.js file that I import on demand, and just manipulate it.
You just have to change BrowserRouter to Router, and specify the history prop. This doesn't change anything for you, except that you have your own history object that you can manipulate as you want.
You need to install history, the library used by react-router.
Example usage, ES6 notation:
history.js
import createBrowserHistory from 'history/createBrowserHistory'
export default createBrowserHistory()
BasicComponent.js
import React, { Component } from 'react';
import history from './history';
class BasicComponent extends Component {
goToIndex(e){
e.preventDefault();
history.push('/');
}
render(){
return <a href="#" onClick={this.goToIndex}>Previous</a>;
}
}
If you have to navigate from a component that is actually rendered from a Route component, you can also access history from props, like that:
BasicComponent.js
import React, { Component } from 'react';
class BasicComponent extends Component {
navigate(e){
e.preventDefault();
this.props.history.push('/url');
}
render(){
return <a href="#" onClick={this.navigate}>Previous</a>;
}
}
Here's how you do this with react-router v2.0.0 with ES6. react-router has moved away from mixins.
import React from 'react';
export default class MyComponent extends React.Component {
navigateToPage = () => {
this.context.router.push('/my-route')
};
render() {
return (
<button onClick={this.navigateToPage}>Go!</button>
);
}
}
MyComponent.contextTypes = {
router: React.PropTypes.object.isRequired
}
For this one, who does not control the server side and because of this is using hash router v2:
Place your history into separate file (e.g. app_history.js ES6):
import { useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false });
export default appHistory;
And use it everywhere!
Your entry point for react-router (app.js ES6):
import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Redirect } from 'react-router'
import appHistory from './app_history'
...
const render((
<Router history={appHistory}>
...
</Router>
), document.querySelector('[data-role="app"]'));
Your navigation inside any component (ES6):
import appHistory from '../app_history'
...
ajaxLogin('/login', (err, data) => {
if (err) {
console.error(err); // login failed
} else {
// logged in
appHistory.replace('/dashboard'); // or .push() if you don't need .replace()
}
})
React Router v6
I haven't touched React in a while, but want to thank and highlight the comment below by Shimrit Snapir:
on React-Router 6.0 <Redirect /> changed to <Navigate />
React Router V4
tl:dr;
if (navigate) {
return <Redirect to="/" push={true} />
}
The simple and declarative answer is that you need to use <Redirect to={URL} push={boolean} /> in combination with setState()
push: boolean - when true, redirecting will push a new entry onto the history instead of replacing the current one.
import { Redirect } from 'react-router'
class FooBar extends React.Component {
state = {
navigate: false
}
render() {
const { navigate } = this.state
// Here is the important part
if (navigate) {
return <Redirect to="/" push={true} />
}
// ^^^^^^^^^^^^^^^^^^^^^^^
return (
<div>
<button onClick={() => this.setState({ navigate: true })}>
Home
</button>
</div>
)
}
}
A full example is here. Read more here.
PS. The example uses ES7+ Property Initializers to initialise state. Look here as well, if you're interested.
Warning: this answer covers only ReactRouter versions before 1.0
I will update this answer with 1.0.0-rc1 use cases after!
You can do this without mixins too.
let Authentication = React.createClass({
contextTypes: {
router: React.PropTypes.func
},
handleClick(e) {
e.preventDefault();
this.context.router.transitionTo('/');
},
render(){
return (<div onClick={this.handleClick}>Click me!</div>);
}
});
The gotcha with contexts is that it is not accessible unless you define the contextTypes on the class.
As for what is context, it is an object, like props, that are passed down from parent to child, but it is passed down implicitly, without having to redeclare props each time. See https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html
Here's the simplest and cleanest way to do it, circa current React-Router 3.0.0 and ES6:
React-Router 3.x.x with ES6:
import { withRouter } from 'react-router';
class Example extends React.Component {
// use `this.props.router.push('/some/path')` here
};
// Export the decorated class
export default withRouter(Example);
Or, if it's not your default class, export like:
withRouter(Example);
export { Example };
Note that in 3.x.x, the <Link> component itself is using router.push, so you can pass it anything you would pass the <Link to= tag, like:
this.props.router.push({pathname: '/some/path', query: {key1: 'val1', key2: 'val2'})'
To do the navigation programmatically, you need to push a new history to the props.history in your component, so something like this can do the work for you:
//using ES6
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick(e) {
e.preventDefault()
/* Look at here, you can add it here */
this.props.history.push('/redirected');
}
render() {
return (
<div>
<button onClick={this.handleClick}>
Redirect!!!
</button>
</div>
)
}
}
export default App;
For ES6 + React components, the following solution worked for me.
I followed Felippe skinner, but added an end to end solution to help beginners like me.
Below are the versions I used:
"react-router": "^2.7.0"
"react": "^15.3.1"
Below is my react component where I used programmatic navigation using react-router:
import React from 'react';
class loginComp extends React.Component {
constructor( context) {
super(context);
this.state = {
uname: '',
pwd: ''
};
}
redirectToMainPage(){
this.context.router.replace('/home');
}
render(){
return <div>
// skipping html code
<button onClick={this.redirectToMainPage.bind(this)}>Redirect</button>
</div>;
}
};
loginComp.contextTypes = {
router: React.PropTypes.object.isRequired
}
module.exports = loginComp;
Below is the configuration for my router:
import { Router, Route, IndexRedirect, browserHistory } from 'react-router'
render(<Router history={browserHistory}>
<Route path='/' component={ParentComp}>
<IndexRedirect to = "/login"/>
<Route path='/login' component={LoginComp}/>
<Route path='/home' component={HomeComp}/>
<Route path='/repair' component={RepairJobComp} />
<Route path='/service' component={ServiceJobComp} />
</Route>
</Router>, document.getElementById('root'));
It may not be the best approach but... Using react-router v4, the following TypeScript code could give an idea for some.
In the rendered component below, e.g. LoginPage, router object is accessible and just call router.transitionTo('/homepage') to navigate.
Navigation code was taken from.
"react-router": "^4.0.0-2",
"react": "^15.3.1",
import Router from 'react-router/BrowserRouter';
import { History } from 'react-history/BrowserHistory';
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
interface MatchWithPropsInterface {
component: typeof React.Component,
router: Router,
history: History,
exactly?: any,
pattern: string
}
class MatchWithProps extends React.Component<MatchWithPropsInterface,any> {
render() {
return(
<Match {...this.props} render={(matchProps) => (
React.createElement(this.props.component, this.props)
)}
/>
)
}
}
ReactDOM.render(
<Router>
{({ router }) => (
<div>
<MatchWithProps exactly pattern="/" component={LoginPage} router={router} history={history} />
<MatchWithProps pattern="/login" component={LoginPage} router={router} history={history} />
<MatchWithProps pattern="/homepage" component={HomePage} router={router} history={history} />
<Miss component={NotFoundView} />
</div>
)}
</Router>,
document.getElementById('app')
);
In React Router v4, I follow these two ways to route programmatically.
this.props.history.push("/something/something")
this.props.history.replace("/something/something")
Number two
Replaces the current entry on the history stack
To get history in props you may have to wrap your component with
withRouter
In React Router v6
import { useNavigate } from "react-router-dom";
function Invoices() {
let navigate = useNavigate();
return (
<div>
<NewInvoiceForm
onSubmit={async event => {
let newInvoice = await createInvoice(event.target);
navigate(`/invoices/${newInvoice.id}`);
}}
/>
</div>
);
}
Getting Started with React Router v6
In React-Router v4 and ES6
You can use withRouter and this.props.history.push.
import {withRouter} from 'react-router-dom';
class Home extends Component {
componentDidMount() {
this.props.history.push('/redirect-to');
}
}
export default withRouter(Home);
To use withRouter with a class-based component, try something like this below.
Don't forget to change the export statement to use withRouter:
import { withRouter } from 'react-router-dom'
class YourClass extends React.Component {
yourFunction = () => {
doSomeAsyncAction(() =>
this.props.history.push('/other_location')
)
}
render() {
return (
<div>
<Form onSubmit={ this.yourFunction } />
</div>
)
}
}
export default withRouter(YourClass);
With React-Router v4 on the horizon, there is now a new way of doing this.
import { MemoryRouter, BrowserRouter } from 'react-router';
const navigator = global && global.navigator && global.navigator.userAgent;
const hasWindow = typeof window !== 'undefined';
const isBrowser = typeof navigator !== 'undefined' && navigator.indexOf('Node.js') === -1;
const Router = isBrowser ? BrowserRouter : MemoryRouter;
<Router location="/page-to-go-to"/>
react-lego is an example app that shows how to use/update react-router and it includes example functional tests which navigate the app.
Based on the previous answers from José Antonio Postigo and Ben Wheeler:
The novelty? Is to be written in TypeScript and uses decorators
or a static property/field
import * as React from "react";
import Component = React.Component;
import { withRouter } from "react-router";
export interface INavigatorProps {
router?: ReactRouter.History.History;
}
/**
* Note: goes great with mobx
* #inject("something") #withRouter #observer
*/
#withRouter
export class Navigator extends Component<INavigatorProps, {}>{
navigate: (to: string) => void;
constructor(props: INavigatorProps) {
super(props);
let self = this;
this.navigate = (to) => self.props.router.push(to);
}
render() {
return (
<ul>
<li onClick={() => this.navigate("/home")}>
Home
</li>
<li onClick={() => this.navigate("/about")}>
About
</li>
</ul>
)
}
}
/**
* Non decorated
*/
export class Navigator2 extends Component<INavigatorProps, {}> {
static contextTypes = {
router: React.PropTypes.object.isRequired,
};
navigate: (to: string) => void;
constructor(props: INavigatorProps, context: any) {
super(props, context);
let s = this;
this.navigate = (to) =>
s.context.router.push(to);
}
render() {
return (
<ul>
<li onClick={() => this.navigate("/home")}>
Home
</li>
<li onClick={() => this.navigate("/about")}>
About
</li>
</ul>
)
}
}
with whatever npm installed today.
"react-router": "^3.0.0" and
"#types/react-router": "^2.0.41"
For those who are already using React Router v6, this can be done using useNavigate hook provided by react-router.
Navigation with this hook is pretty simple:
import { generatePath, useNavigate } from 'react-router';
navigate(-1); // navigates back
navigate('/my/path'); // navigates to a specific path
navigate(generatePath('my/path/:id', { id: 1 })); // navigates to a dynamic path, generatePath is very useful for url replacements
For Latest react-router-dom v6
useHistory() is replaced with useNavigate().
You need to use:
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
navigate('/your-page-link');
With the current React version (15.3), this.props.history.push('/location'); worked for me, but it showed the following warning:
browser.js:49 Warning: [react-router] props.history and
context.history are deprecated. Please use context.router.
and I solved it using context.router like this:
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.backPressed = this.backPressed.bind(this);
}
backPressed() {
this.context.router.push('/back-location');
}
...
}
MyComponent.contextTypes = {
router: React.PropTypes.object.isRequired
};
export default MyComponent;
React Router v6 with hooks
import {useNavigate} from 'react-router-dom';
let navigate = useNavigate();
navigate('home');
And to move across the browser history,
navigate(-1); ---> Go back
navigate(1); ---> Go forward
navigate(-2); ---> Move two steps backward.
If you are using hash or browser history then you can do
hashHistory.push('/login');
browserHistory.push('/login');
React-Router V4
If you're using version 4 then you can use my library (shameless plug) where you simply dispatch an action and everything just works!
dispatch(navigateTo("/aboutUs"));
trippler
Those who are facing issues in implementing this in React Router v4.
Here is a working solution for navigating through the React app from redux actions.
File history.js
import createHistory from 'history/createBrowserHistory'
export default createHistory()
Files App.js/Route.jsx
import { Router, Route } from 'react-router-dom'
import history from './history'
...
<Router history={history}>
<Route path="/test" component={Test}/>
</Router>
File *another_file.js or redux file
import history from './history'
history.push('/test') // This should change the URL and rerender Test component
All thanks to this comment on GitHub:
ReactTraining issues comment
You can also use the useHistory hook in a stateless component. Example from the documentation:
import { useHistory } from "react-router"
function HomeButton() {
const history = useHistory()
return (
<button type="button" onClick={() => history.push("/home")}>
Go home
</button>
)
}
Note: Hooks were added in react-router#5.1.0 and require react#>=16.8
Programmatically navigate in class-based components.
import { Redirect } from "react-router-dom";
class MyComponent extends React.Component{
state = {rpath: null}
const goTo = (path) => this.setState({rpath: path});
render(){
if(this.state.rpath){
return <Redirect to={this.state.rpath}/>
}
.....
.....
}
}
In my answer there are three different ways to redirect programmatically to a route. Some of the solutions has been presented already, but the following ones focused only for functional components with an additional demo application.
Using the following versions:
react: 16.13.1
react-dom: 16.13.1
react-router: 5.2.0
react-router-dom: 5.2.0
typescript: 3.7.2
Configuration:
So first of all the solution is using HashRouter, configured as follows:
<HashRouter>
// ... buttons for redirect
<Switch>
<Route exact path="/(|home)" children={Home} />
<Route exact path="/usehistory" children={UseHistoryResult} />
<Route exact path="/withrouter" children={WithRouterResult} />
<Route exact path="/redirectpush" children={RedirectPushResult} />
<Route children={Home} />
</Switch>
</HashRouter>
From the documentation about <HashRouter>:
A <Router> that uses the hash portion of the URL (i.e. window.location.hash) to keep your UI in sync with the URL.
Solutions:
Using <Redirect> to push using useState:
Using in a functional component (RedirectPushAction component from my repository) we can use useState to handle redirect. The tricky part is once the redirection happened, we need to set the redirect state back to false. By using setTimeOut with 0 delay we are waiting until React commits Redirect to the DOM and then getting back the button in order to use it the next time.
Please find my example below:
const [redirect, setRedirect] = useState(false);
const handleRedirect = useCallback(() => {
let render = null;
if (redirect) {
render = <Redirect to="/redirectpush" push={true} />
// In order wait until committing to the DOM
// and get back the button for clicking next time
setTimeout(() => setRedirect(false), 0);
}
return render;
}, [redirect]);
return <>
{handleRedirect()}
<button onClick={() => setRedirect(true)}>
Redirect push
</button>
</>
From <Redirect> documentation:
Rendering a <Redirect> will navigate to a new location. The new location will override the current location in the history stack, like server-side redirects (HTTP 3xx) do.
Using useHistory hook:
In my solution there is a component called UseHistoryAction which represents the following:
let history = useHistory();
return <button onClick={() => { history.push('/usehistory') }}>
useHistory redirect
</button>
The useHistory hook gives us access to the history object which helps us programmatically navigate or change routes.
Using withRouter, get the history from props:
Created one component called WithRouterAction, displays as below:
const WithRouterAction = (props:any) => {
const { history } = props;
return <button onClick={() => { history.push('/withrouter') }}>
withRouter redirect
</button>
}
export default withRouter(WithRouterAction);
Reading from withRouter documentation:
You can get access to the history object's properties and the closest <Route>'s match via the withRouter higher-order component. withRouter will pass updated match, location, and history props to the wrapped component whenever it renders.
Demo:
For better representation I have built a GitHub repository with these examples, please find it below:
React Router Programmatically Redirect Examples
The right answer was for me at the time of writing
this.context.router.history.push('/');
But you need to add PropTypes to your component
Header.contextTypes = {
router: PropTypes.object.isRequired
}
export default Header;
Don't forget to import PropTypes
import PropTypes from 'prop-types';
Maybe not the best solution, but it gets the job done:
import { Link } from 'react-router-dom';
// Create functional component Post
export default Post = () => (
<div className="component post">
<button className="button delete-post" onClick={() => {
// ... delete post
// then redirect, without page reload, by triggering a hidden Link
document.querySelector('.trigger.go-home').click();
}}>Delete Post</button>
<Link to="/" className="trigger go-home hidden"></Link>
</div>
);
Basically, logic tied to one action (in this case a post deletion) will end up calling a trigger for redirect. This is not ideal, because you will add a DOM node 'trigger' to your markup just so you can conveniently call it when needed. Also, you will directly interact with the DOM, which in a React component may not be desired.
Still, this type of redirect is not required that often. So one or two extra, hidden links in your component markup would not hurt that much, especially if you give them meaningful names.
If you happen to pair RR4 with redux through react-router-redux, using the routing action creators from react-router-redux is an option as well.
import { push, replace, ... } from 'react-router-redux'
class WrappedComponent extends React.Component {
handleRedirect(url, replaceState = true) {
replaceState
? this.props.dispatch(replace(url))
: this.props.dispatch(push(url))
}
render() { ... }
}
export default connect(null)(WrappedComponent)
If you use redux thunk/saga to manage async flow, import the above action creators in redux actions and hook to React components using mapDispatchToProps might be better.

react-router (v4) how to go back?

Trying to figure out how can I go back to the previous page. I am using [react-router-v4][1]
This is the code I have configured in my first landing page:
<Router>
<div>
<Link to="/"><div className="routerStyle"><Glyphicon glyph="home" /></div></Link>
<Route exact path="/" component={Page1}/>
<Route path="/Page2" component={Page2}/>
<Route path="/Page3" component={Page3}/>
</div>
</Router>
In order to forward to subsequent pages, I simply do:
this.props.history.push('/Page2');
However, how can I go back to previous page?
Tried few things like mentioned below but no luck:
1. this.props.history.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.props')
this.context.router.goBack();
Gives error:
TypeError: null is not an object (evaluating 'this.context')
this.props.history.push('/');
Gives error:
TypeError: null is not an object (evaluating 'this.props')
Posting the Page1 code here below:
import React, {Component} from 'react';
import {Button} from 'react-bootstrap';
class Page1 extends Component {
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
}
handleNext() {
this.props.history.push('/page2');
}
handleBack() {
this.props.history.push('/');
}
/*
* Main render method of this class
*/
render() {
return (
<div>
{/* some component code */}
<div className="navigationButtonsLeft">
<Button onClick={this.handleBack} bsStyle="success">< Back</Button>
</div>
<div className="navigationButtonsRight">
<Button onClick={this.handleNext} bsStyle="success">Next ></Button>
</div>
</div>
);
}
export default Page1;
I think the issue is with binding:
constructor(props){
super(props);
this.goBack = this.goBack.bind(this); // i think you are missing this
}
goBack(){
this.props.history.goBack();
}
.....
<button onClick={this.goBack}>Go Back</button>
As I have assumed before you posted the code:
constructor(props) {
super(props);
this.handleNext = this.handleNext.bind(this);
this.handleBack = this.handleBack.bind(this); // you are missing this line
}
UPDATED:
Now we have hook, so we can do it easily by using useHistory
const history = useHistory()
const goBack = () => {
history.goBack()
}
return (
<button type="button" onClick={goBack}>
Go back
</button>
);
ORIGINAL POST:
this.props.history.goBack();
This is the correct solution for react-router v4
But one thing you should keep in mind is that you need to make sure this.props.history is existed.
That means you need to call this function this.props.history.goBack(); inside the component that is wrapped by < Route/>
If you call this function in a component that deeper in the component tree, it will not work.
EDIT:
If you want to have history object in the component that is deeper in the component tree (which is not wrapped by < Route>), you can do something like this:
...
import {withRouter} from 'react-router-dom';
class Demo extends Component {
...
// Inside this you can use this.props.history.goBack();
}
export default withRouter(Demo);
For use with React Router v4 and a functional component anywhere in the dom-tree.
import React from 'react';
import { withRouter } from 'react-router-dom';
const GoBack = ({ history }) => <img src="./images/back.png" onClick={() => history.goBack()} alt="Go back" />;
export default withRouter(GoBack);
Each answer here has parts of the total solution. Here's the complete solution that I used to get it to work inside of components deeper than where Route was used:
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
^ You need that second line to import function and to export component at bottom of page.
render() {
return (
...
<div onClick={() => this.props.history.goBack()}>GO BACK</div>
)
}
^ Required the arrow function vs simply onClick={this.props.history.goBack()}
export default withRouter(MyPage)
^ wrap your component's name with 'withRouter()'
Here is the cleanest and simplest way you can handle this problem, which also nullifies the probable pitfalls of the this keyword. Use functional components:
import { withRouter } from "react-router-dom";
wrap your component or better App.js with the withRouter() HOC this makes history to be available "app-wide". wrapping your component only makes history available for that specific component``` your choice.
So you have:
export default withRouter(App);
In a Redux environment export default withRouter( connect(mapStateToProps, { <!-- your action creators -->})(App), ); you should even be able to user history from your action creators this way.
in your component do the following:
import {useHistory} from "react-router-dom";
const history = useHistory(); // do this inside the component
goBack = () => history.goBack();
<btn btn-sm btn-primary onclick={goBack}>Go Back</btn>
export default DemoComponent;
Gottcha useHistory is only exported from the latest v5.1 react-router-dom so be sure to update the package. However, you should not have to worry.
about the many snags of the this keyword.
You can also make this a reusable component to use across your app.
function BackButton({ children }) {
let history = useHistory()
return (
<button type="button" onClick={() => history.goBack()}>
{children}
</button>
)
}```
Cheers.
Can you provide the code where you use this.props.history.push('/Page2');?
Have you tried the goBack() method?
this.props.history.goBack();
It's listed here https://reacttraining.com/react-router/web/api/history
With a live example here https://reacttraining.com/react-router/web/example/modal-gallery
If using react hooks just do:
import { useHistory } from "react-router-dom";
const history = useHistory();
history.go(-1);
UPDATE 2022 w V6
navigate(-1)
to omit the current page from history:
navigate(-1, { replace: true })
Try:
this.props.router.goBack()
Simply use
<span onClick={() => this.props.history.goBack()}>Back</span>
Hope this will help someone:
import React from 'react';
import * as History from 'history';
import { withRouter } from 'react-router-dom';
interface Props {
history: History;
}
#withRouter
export default class YourComponent extends React.PureComponent<Props> {
private onBackClick = (event: React.MouseEvent): void => {
const { history } = this.props;
history.goBack();
};
...
Maybe this can help someone.
I was using history.replace() to redirect, so when i tried to use history.goBack(), i was send to the previous page before the page i was working with.
So i changed the method history.replace() to history.push() so the history could be saved and i would be able to go back.
I am not sure if anyone else ran into this problem or may need to see this. But I spent about 3 hours trying to solve this issue:
I wanted to implement a simple goBack() on the click of a button. I thought I was off to a good start because my App.js was already wrapped in the Router and I was importing { BrowserRouter as Router } from 'react-router-dom'; ... Since the Router element allows me to assess the history object.
ex:
import React from 'react';
import './App.css';
import Splash from './components/Splash';
import Header from './components/Header.js';
import Footer from './components/Footer';
import Info from './components/Info';
import Timer from './components/Timer';
import Options from './components/Options';
import { BrowserRouter as Router, Route } from 'react-router-dom';
function App() {
return (
<Router>
<Header />
<Route path='/' component={Splash} exact />
<Route path='/home' component={Info} exact />
<Route path='/timer' component={Timer} exact />
<Route path='/options' component={Options} exact />
<Footer />
</Router>
);
}
export default App;
BUT the trouble was on my Nav (a child component) module,
I had to 'import { withRouter } from 'react-router-dom';'
and then force an export with:
export default withRouter(Nav);
ex:
import React from 'react';
import { withRouter } from 'react-router-dom';
class Nav extends React.Component {
render() {
return (
<div>
<label htmlFor='back'></label>
<button id='back' onClick={ () => this.props.history.goBack() }>Back</button>
<label htmlFor='logOut'></label>
<button id='logOut' ><a href='./'>Log-Out</a>
</button>
</div>
);
}
}
export default withRouter(Nav);
in summary, withRouter was created because of a known issue in React where in certain scenarios when inheritance from a router is refused, a forced export is necessary.
You can use history.goBack() in functional component. Just like this.
import { useHistory } from 'react-router';
const component = () => {
const history = useHistory();
return (
<button onClick={() => history.goBack()}>Previous</button>
)
}

How to manually invoke Link in React-router?

I have a component that receives through props a <Link/> object from react-router. Whenever the user clicks on a 'next' button inside this component I want to invoke <Link/> object manually.
Right now, I'm using refs to access the backing instance and manually clicking on the 'a' tag that <Link/> generates.
Question: Is there a way to manually invoke the Link (e.g. this.props.next.go)?
This is the current code I have:
//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
<Document next={sampleLink} />
//in Document.js
...
var Document = React.createClass({
_onClickNext: function() {
var next = this.refs.next.getDOMNode();
next.querySelectorAll('a').item(0).click(); //this sounds like hack to me
},
render: function() {
return (
...
<div ref="next">{this.props.next} <img src="rightArrow.png" onClick={this._onClickNext}/></div>
...
);
}
});
...
This is the code I would like to have:
//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
<Document next={sampleLink} />
//in Document.js
...
var Document = React.createClass({
render: function() {
return (
...
<div onClick={this.props.next.go}>{this.props.next.label} <img src="rightArrow.png" /> </div>
...
);
}
});
...
React Router v6 - React 17+ (updated 01/14/2022)
import React, {useCallback} from 'react';
import {useNavigate} from 'react-router-dom';
export default function StackOverflowExample() {
const navigate = useNavigate();
const handleOnClick = useCallback(() => navigate('/sample', {replace: true}), [navigate]);
return (
<button type="button" onClick={handleOnClick}>
Go home
</button>
);
}
Note: For this answer, the one major change between v6 and v5 is useNavigate is now the preferred React hook. useHistory is deprecated and not recommended.
React Router v5 - React 16.8+ with Hooks
If you're leveraging React Hooks, you can take advantage of the useHistory API that comes from React Router v5.
import React, {useCallback} from 'react';
import {useHistory} from 'react-router-dom';
export default function StackOverflowExample() {
const history = useHistory();
const handleOnClick = useCallback(() => history.push('/sample'), [history]);
return (
<button type="button" onClick={handleOnClick}>
Go home
</button>
);
}
Another way to write the click handler if you don't want to use useCallback
const handleOnClick = () => history.push('/sample');
React Router v4 - Redirect Component
The v4 recommended way is to allow your render method to catch a redirect. Use state or props to determine if the redirect component needs to be shown (which then trigger's a redirect).
import { Redirect } from 'react-router';
// ... your class implementation
handleOnClick = () => {
// some action...
// then redirect
this.setState({redirect: true});
}
render() {
if (this.state.redirect) {
return <Redirect push to="/sample" />;
}
return <button onClick={this.handleOnClick} type="button">Button</button>;
}
Reference: https://reacttraining.com/react-router/web/api/Redirect
React Router v4 - Reference Router Context
You can also take advantage of Router's context that's exposed to the React component.
static contextTypes = {
router: PropTypes.shape({
history: PropTypes.shape({
push: PropTypes.func.isRequired,
replace: PropTypes.func.isRequired
}).isRequired,
staticContext: PropTypes.object
}).isRequired
};
handleOnClick = () => {
this.context.router.push('/sample');
}
This is how <Redirect /> works under the hood.
Reference: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Redirect.js#L46,L60
React Router v4 - Externally Mutate History Object
If you still need to do something similar to v2's implementation, you can create a copy of BrowserRouter then expose the history as an exportable constant. Below is a basic example but you can compose it to inject it with customizable props if needed. There are noted caveats with lifecycles, but it should always rerender the Router, just like in v2. This can be useful for redirects after an API request from an action function.
// browser router file...
import createHistory from 'history/createBrowserHistory';
import { Router } from 'react-router';
export const history = createHistory();
export default class BrowserRouter extends Component {
render() {
return <Router history={history} children={this.props.children} />
}
}
// your main file...
import BrowserRouter from './relative/path/to/BrowserRouter';
import { render } from 'react-dom';
render(
<BrowserRouter>
<App/>
</BrowserRouter>
);
// some file... where you don't have React instance references
import { history } from './relative/path/to/BrowserRouter';
history.push('/sample');
Latest BrowserRouter to extend: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/BrowserRouter.js
React Router v2
Push a new state to the browserHistory instance:
import {browserHistory} from 'react-router';
// ...
browserHistory.push('/sample');
Reference: https://github.com/reactjs/react-router/blob/master/docs/guides/NavigatingOutsideOfComponents.md
React Router 4 includes a withRouter HOC that gives you access to the history object via this.props:
import React, {Component} from 'react'
import {withRouter} from 'react-router-dom'
class Foo extends Component {
constructor(props) {
super(props)
this.goHome = this.goHome.bind(this)
}
goHome() {
this.props.history.push('/')
}
render() {
<div className="foo">
<button onClick={this.goHome} />
</div>
}
}
export default withRouter(Foo)
In the version 5.x, you can use useHistory hook of react-router-dom:
// Sample extracted from https://reacttraining.com/react-router/core/api/Hooks/usehistory
import { useHistory } from "react-router-dom";
function HomeButton() {
const history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
https://github.com/rackt/react-router/blob/bf89168acb30b6dc9b0244360bcbac5081cf6b38/examples/transitions/app.js#L50
or you can even try executing onClick this (more violent solution):
window.location.assign("/sample");
Answers here are outdated.
React Router 6
useHistory is deprecated v6 uses the useNavigate hook instead.
import { useNavigate } from 'react-router-dom'
const navigate = useNavigate()
navigate(`/somewhere`, { replace: true })
Ok, I think I was able to find a proper solution for that.
Now, instead of sending <Link/> as prop to Document, I send <NextLink/> which is a custom wrapper for the react-router Link. By doing that, I'm able to have the right arrow as part of the Link structure while still avoiding to have routing code inside Document object.
The updated code looks like follows:
//in NextLink.js
var React = require('react');
var Right = require('./Right');
var NextLink = React.createClass({
propTypes: {
link: React.PropTypes.node.isRequired
},
contextTypes: {
transitionTo: React.PropTypes.func.isRequired
},
_onClickRight: function() {
this.context.transitionTo(this.props.link.props.to);
},
render: function() {
return (
<div>
{this.props.link}
<Right onClick={this._onClickRight} />
</div>
);
}
});
module.exports = NextLink;
...
//in MasterPage.js
var sampleLink = <Link to="/sample">Go To Sample</Link>
var nextLink = <NextLink link={sampleLink} />
<Document next={nextLink} />
//in Document.js
...
var Document = React.createClass({
render: function() {
return (
...
<div>{this.props.next}</div>
...
);
}
});
...
P.S: If you are using the latest version of react-router you may need to use this.context.router.transitionTo instead of this.context.transitionTo. This code will work fine for react-router version 0.12.X.
React Router 4
You can easily invoke the push method via context in v4:
this.context.router.push(this.props.exitPath);
where context is:
static contextTypes = {
router: React.PropTypes.object,
};
If you'd like to extend the Link component to utilise some of the logic in it's onClick() handler, here's how:
import React from 'react';
import { Link } from "react-router-dom";
// Extend react-router-dom Link to include a function for validation.
class LinkExtra extends Link {
render() {
const linkMarkup = super.render();
const { validation, ...rest} = linkMarkup.props; // Filter out props for <a>.
const onclick = event => {
if (!this.props.validation || this.props.validation()) {
this.handleClick(event);
} else {
event.preventDefault();
console.log("Failed validation");
}
}
return(
<a {...rest} onClick={onclick} />
)
}
}
export default LinkExtra;
Usage
<LinkExtra to="/mypage" validation={() => false}>Next</LinkExtra>
again this is JS :) this still works ....
var linkToClick = document.getElementById('something');
linkToClick.click();
<Link id="something" to={/somewhaere}> the link </Link>

Categories