how to access history object in new React Router v4 - javascript

I have tried all the suggested ways in other threads and it is still not working which is why I am posting the question.
So I have history.js looking like this
import { createBrowserHistory } from 'history';
export default createBrowserHistory;
My index.js
render((
<Router history={history}>
<div>
<Route component={App}/>
</div>
</Router>
), document.getElementById('root')
);
Home.js Looks like this
class Home extends Component {
handleSubmit(e) {
e.preventDefault();
let teacherName = e.target.elements[0].value;
let teacherTopic = e.target.elements[1].value;
let path = `/featured/${teacherName}/${teacherTopic}`;
history.push(path);
}
render() {
return (
<div className="main-content home">
<hr />
<h3>Featured Teachers</h3>
<form onSubmit={this.handleSubmit}>
<input type="text" placeholder="Name"/>
<input type="text" placeholder="Topic"/>
<button type="submit"> Submit </button>
</form>
</div>
);
}
}
export default Home;
Home.js is actually routed in app.js which is routed in index.js.
The problem is that I cannot access history object anywhere.
The ways I have tried are as below
1) this.context.history.push(path) in home.js - cannot access context of undefined
2) this.props.history.push(path) in home.js - cannot access props of undefined
3) browserHistory.push(path) in index.js - this is deprecated now
4) the way above - _history2.default.push is not a function
None of the methods that I have tried above works. The second was the strangest as I should be able to access history object as props anywhere according to the documentation https://reacttraining.com/react-router/web/api/Route/Route-render-methods
I know the previous v2 or v3 way of accessing history is also deprecated.
So can someone who knows how to access history object in React Router v4 answer this question? Thanks.

Looks to me like you might be missing a withRouter connection. this would make the history props available to this component
...
import { withRouter } from 'react-router'
class Home extends Component {
functionMethod() {
const { history: { push } } = this.props;
doStuff();
push('/location');
}
...
}
export default withRouter(Home);
https://reacttraining.com/react-router/web/api/withRouter

use the hook: useHistory
import { useHistory } from "react-router-dom";
function HomeButton() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}

I had this problem for a while and it was simply not having the withRouter connection.
import { withRouter } from 'react-router';
class Sample extends Component{
render()
{
<button type="button" onClick={()=>{
this.props.history.push('/');
window.location.reload(); }}>
}
}
export default withRouter(Sample);
`

Related

Redirecting using React Router shows blank page

I'm trying to build a simple example project where the user is redirected to the 'contact' page upon clicking a button, using React. I'm trying to achieve this by setting the value of a state property. When I run the code I have, it does change the browser address bar URL to that of the contact page, but does not seem to actually load the component - I get a blank page instead. If I manually navigate to that URL (http://localhost:3000/contact) I can see the contents.
Here are my App.js and Contact.js files -
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Switch, Route, Link, Redirect } from 'react-router-dom';
import Contact from './Contact';
class App extends Component {
state = {
redirect: false
}
setRedirect = () => {
this.setState({
redirect: true
})
}
renderRedirect = () => {
if (this.state.redirect) {
return <Redirect to='/contact' />
}
}
render() {
return (
<Router>
<Switch>
<Route exact path='/contact' component={Contact} />
</Switch>
<div>
{this.renderRedirect()}
<button onClick={this.setRedirect}>Redirect</button>
</div>
</Router>
)
}
}
export default App;
Contact.js
import React, { Component } from 'react';
class Contact extends Component {
render() {
return (
<div>
<h2>Contact Me</h2>
<input type="text"></input>
</div>
);
}
}
export default Contact;
Using state isn't really a requirement for me, so other (preferably simpler) methods of redirection would be appreciated too.
Since your button is nothing more than a link, you could replace it with:
<Link to="/contact">Redirect</Link>
There are many alternatives though, you could for example look into BrowserRouter's browserHistory:
import { browserHistory } from 'react-router'
browserHistory.push("/contact")
Or perhaps this.props.history.push("/contact").
There are pros and cons to every method, you'll have to look into each and see which you prefer.
I got here for a similiar situation. It's possible use withRouter (https://reactrouter.com/web/api/withRouter) to handle that.
This example was tested with "react": "^16.13.1","react-router-dom": "^5.2.0" and "history": "^5.0.0" into "dependecies" sections in package.json file.
In App.js I have the BrowserRouter (usually people import BrowserRouter as Router, I prefer work with original names) with Home and Contact.
App.js
import React, { Component } from "react";
import {
BrowserRouter,
Switch,
Route,
} from "react-router-dom";
import Home from "./pages/Home";
import Contact from "./pages/Contact";
class App extends Component
{
// stuff...
render()
{
return (
<BrowserRouter>
<Switch>
<Route path="/contact">
<Contact />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</BrowserRouter>
);
}
}
export default App;
ASIDE 1: The Route with path="/contact" is placed before path="/" because Switch render the first match, so put Home at the end. If you have path="/something" and path="/something/:id" place the more specific route (with /:id in this case) before. (https://reactrouter.com/web/api/Switch)
ASIDE 2: I'm using class component but I believe (I didn't test it) a functional component will also work.
In Home.js and Contact.js I use withRouter associated with export keyword. This makes Home and Contact components receive the history object of BrowserRouter via props. Use method push() to add "/contact" and "/" to the history stack. (https://reactrouter.com/web/api/history).
Home.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const Home = ( props ) =>
{
return (
<div>
Home!
<button
onClick={ () => props.history.push( "/contact" ) }
>
Get in Touch
<button>
</div>
);
}
export default withRouter( Home );
Contact.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const Contact = ( props ) =>
{
return (
<div>
Contact!
<button
onClick={ () => props.history.push( "/" ) }
>
Go Home
<button>
</div>
);
}
export default withRouter( Contact );
Particularly, I'm using also in a BackButton component with goBack() to navigate backwards:
BackButton.js
import React from "react";
import {
withRouter
} from "react-router-dom";
export const BackButton = ( props ) =>
{
return (
<button
onClick={ () => props.history.goBack() }
>
Go back
<button>
);
}
export default withRouter( BackButton );
So I could modify the Contact to:
Contact.js (with BackButton)
import React from "react";
import BackButton from "../components/BackButton";
export const Contact = ( props ) =>
{
return (
<div>
Contact!
<BackButton />
</div>
);
}
export default Contact; // now I'm not using history in this file.
// the navigation responsability is inside BackButton component.
Above was the best solution for me. Other possible solutions are:
useHistory Hook (https://reactrouter.com/web/api/Hooks)
work with Router instead BrowserRouter - (https://reactrouter.com/web/api/Router)

unable to do routing using react getting errors as Cannot read property 'props' of undefined

Hi i am a newbie to react what i am trying is to navigate from one component to another and for that i used the below logic
class App extends Component {
getData(){
this.props.history.push('/test-data');
}
render() {
return (
<div>
<h1>ReactJS</h1>
<div >
<button id="data" onClick={this.getData}>test data</button>
<button id="con">test info</button>
</div>
<BrowserRouter>
<div>
<Route path="/test-data" component={TestData} />
<Route path="/test-info" component={TestInfo} />
</div>
</BrowserRouter>
</div>
)
}
}
when ever i try to navigate to the page i am getting below error
Cannot read property 'props' of undefined
below is my stackblitz code :-https://stackblitz.com/edit/react-xtshvq
There are couple of issues with the code.
Change getData to an arrow function. And to access history, you need to wrap your component in withRouter HOC.
Here is the working code: https://stackblitz.com/edit/react-jmgjf4?file=index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Link, withRouter } from "react-router-dom";
import TestData from './test-data/test-data';
import TestInfo from './test-info/test-info';
class App extends Component {
getData = () => {
this.props.history.push('/test-data');
}
render() {
return (
<div>
<h1>ReactJS</h1>
<div >
<button id="data" onClick={this.getData}>test data</button>
<button id="con">test info</button>
</div>
<div>
<Route path="/test-data" component={TestData} />
<Route path="/test-info" component={TestInfo} />
</div>
</div>
)
}
}
const AppWithRouter = withRouter(App)
const AppContainer = () => {
return (
<BrowserRouter>
<AppWithRouter />
</BrowserRouter>
)
}
ReactDOM.render(<AppContainer />, document.querySelector('#root'));
Error Wrap <BrowserRouter> in index.js as
//index.js
ReactDOM.render(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root'));
For demo Application check this link
You can fix the error by adding the following code to the App class:
constructor() {
this.getData = this.getData.bind(this)
}
The problem is the this keyword. In JavaScript it's really confusing and leads to those issues. When you pass the function this.getData to the button component, it tries to execute it within the scope of the button component which then fails.
What the above code does is it basically tells the function to use the passed variable as the internal this inside the function.
Your getData function is not bound to the this context, so it can´t access this.props. You have to either bind it or use an arrow function:
// Bind it in the constructor
constructor(props) {
super(props);
this.getData = this.getData.bind(this);
}
// Or use arrow function
getData = () => {};
Your App component has no access to the history prop because it never gets any props. To change this you have to move your BrowserRouter outside of the App component. You can add it at ReactDOM.render. Then you have to wrap your App component with the withRouter HOC from react-router and use that wrapped component as a child of the BrowserRouter component:
import { withRouter } from 'react-router';
const AppWithRouter = withRouter(App);
ReactDOM.render(<BrowserRouter><AppWithRouter /></BrowserRouter>, document.querySelector('#root'));

React-Router V2, V4 - How can I use "router" of react-router V2 in react-router V4? [duplicate]

In the current version of React Router (v3) I can accept a server response and use browserHistory.push to go to the appropriate response page. However, this isn't available in v4, and I'm not sure what the appropriate way to handle this is.
In this example, using Redux, components/app-product-form.js calls this.props.addProduct(props) when a user submits the form. When the server returns a success, the user is taken to the Cart page.
// actions/index.js
export function addProduct(props) {
return dispatch =>
axios.post(`${ROOT_URL}/cart`, props, config)
.then(response => {
dispatch({ type: types.AUTH_USER });
localStorage.setItem('token', response.data.token);
browserHistory.push('/cart'); // no longer in React Router V4
});
}
How can I make a redirect to the Cart page from function for React Router v4?
You can use the history methods outside of your components. Try by the following way.
First, create a history object used the history package:
// src/history.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
Then wrap it in <Router> (please note, you should use import { Router } instead of import { BrowserRouter as Router }):
// src/index.jsx
// ...
import { Router, Route, Link } from 'react-router-dom';
import history from './history';
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/login">Login</Link></li>
</ul>
<Route exact path="/" component={HomePage} />
<Route path="/login" component={LoginPage} />
</div>
</Router>
</Provider>,
document.getElementById('root'),
);
Change your current location from any place, for example:
// src/actions/userActionCreators.js
// ...
import history from '../history';
export function login(credentials) {
return function (dispatch) {
return loginRemotely(credentials)
.then((response) => {
// ...
history.push('/');
});
};
}
UPD: You can also see a slightly different example in React Router FAQ.
React Router v4 is fundamentally different from v3 (and earlier) and you cannot do browserHistory.push() like you used to.
This discussion seems related if you want more info:
Creating a new browserHistory won't work because <BrowserRouter> creates its own history instance, and listens for changes on that. So a different instance will change the url but not update the <BrowserRouter>.
browserHistory is not exposed by react-router in v4, only in v2.
Instead you have a few options to do this:
Use the withRouter high-order component
Instead you should use the withRouter high order component, and wrap that to the component that will push to history. For example:
import React from "react";
import { withRouter } from "react-router-dom";
class MyComponent extends React.Component {
...
myFunction() {
this.props.history.push("/some/Path");
}
...
}
export default withRouter(MyComponent);
Check out the official documentation for more info:
You can get access to the history object’s properties and the closest <Route>'s match via the withRouter higher-order component. withRouter will re-render its component every time the route changes with the same props as <Route> render props: { match, location, history }.
Use the context API
Using the context might be one of the easiest solutions, but being an experimental API it is unstable and unsupported. Use it only when everything else fails. Here's an example:
import React from "react";
import PropTypes from "prop-types";
class MyComponent extends React.Component {
static contextTypes = {
router: PropTypes.object
}
constructor(props, context) {
super(props, context);
}
...
myFunction() {
this.context.router.history.push("/some/Path");
}
...
}
Have a look at the official documentation on context:
If you want your application to be stable, don't use context. It is an experimental API and it is likely to break in future releases of React.
If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes.
Now with react-router v5 you can use the useHistory hook like this:
import { useHistory } from "react-router-dom";
function HomeButton() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<button type="button" onClick={handleClick}>
Go home
</button>
);
}
read more at: https://reacttraining.com/react-router/web/api/Hooks/usehistory
Simplest way in React Router 4 is to use
this.props.history.push('/new/url');
But to use this method, your existing component should have access to history object. We can get access by
If your component is linked to Route directly, then your component already has access to history object.
eg:
<Route path="/profile" component={ViewProfile}/>
Here ViewProfile has access to history.
If not connected to Route directly.
eg:
<Route path="/users" render={() => <ViewUsers/>}
Then we have to use withRouter, a heigher order fuction to warp the existing component.
Inside ViewUsers component
import { withRouter } from 'react-router-dom';
export default withRouter(ViewUsers);
That's it now, your ViewUsers component has access to history object.
UPDATE
2- in this scenario, pass all route props to your component, and then we can access this.props.history from the component even without a HOC
eg:
<Route path="/users" render={props => <ViewUsers {...props} />}
This is how I did it:
import React, {Component} from 'react';
export default class Link extends Component {
constructor(props) {
super(props);
this.onLogout = this.onLogout.bind(this);
}
onLogout() {
this.props.history.push('/');
}
render() {
return (
<div>
<h1>Your Links</h1>
<button onClick={this.onLogout}>Logout</button>
</div>
);
}
}
Use this.props.history.push('/cart'); to redirect to cart page it will be saved in history object.
Enjoy, Michael.
According to React Router v4 documentation - Redux Deep Integration session
Deep integration is needed to:
"be able to navigate by dispatching actions"
However, they recommend this approach as an alternative to the "deep integration":
"Rather than dispatching actions to navigate you can pass the history object provided to route components to your actions and navigate with it there."
So you can wrap your component with the withRouter high order component:
export default withRouter(connect(null, { actionCreatorName })(ReactComponent));
which will pass the history API to props. So you can call the action creator passing the history as a param. For example, inside your ReactComponent:
onClick={() => {
this.props.actionCreatorName(
this.props.history,
otherParams
);
}}
Then, inside your actions/index.js:
export function actionCreatorName(history, param) {
return dispatch => {
dispatch({
type: SOME_ACTION,
payload: param.data
});
history.push("/path");
};
}
Nasty question, took me quite a lot of time, but eventually, I solved it this way:
Wrap your container with withRouter and pass history to your action in mapDispatchToProps function. In action use history.push('/url') to navigate.
Action:
export function saveData(history, data) {
fetch.post('/save', data)
.then((response) => {
...
history.push('/url');
})
};
Container:
import { withRouter } from 'react-router-dom';
...
const mapDispatchToProps = (dispatch, ownProps) => {
return {
save: (data) => dispatch(saveData(ownProps.history, data))}
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Container));
This is valid for React Router v4.x.
I offer one more solution in case it is worthful for someone else.
I have a history.js file where I have the following:
import createHistory from 'history/createBrowserHistory'
const history = createHistory()
history.pushLater = (...args) => setImmediate(() => history.push(...args))
export default history
Next, on my Root where I define my router I use the following:
import history from '../history'
import { Provider } from 'react-redux'
import { Router, Route, Switch } from 'react-router-dom'
export default class Root extends React.Component {
render() {
return (
<Provider store={store}>
<Router history={history}>
<Switch>
...
</Switch>
</Router>
</Provider>
)
}
}
Finally, on my actions.js I import History and make use of pushLater
import history from './history'
export const login = createAction(
...
history.pushLater({ pathname: PATH_REDIRECT_LOGIN })
...)
This way, I can push to new actions after API calls.
Hope it helps!
this.context.history.push will not work.
I managed to get push working like this:
static contextTypes = {
router: PropTypes.object
}
handleSubmit(e) {
e.preventDefault();
if (this.props.auth.success) {
this.context.router.history.push("/some/Path")
}
}
Be careful that don't use react-router#5.2.0 or react-router-dom#5.2.0 with history#5.0.0. URL will update after history.push or any other push to history instructions but navigation is not working with react-router. use npm install history#4.10.1 to change the history version. see React router not working after upgrading to v 5.
I think this problem is happening when push to history happened. for example using <NavLink to="/apps"> facing a problem in NavLink.js that consume <RouterContext.Consumer>. context.location is changing to an object with action and location properties when the push to history occurs. So currentLocation.pathname is null to match the path.
In this case you're passing props to your thunk. So you can simply call
props.history.push('/cart')
If this isn't the case you can still pass history from your component
export function addProduct(data, history) {
return dispatch => {
axios.post('/url', data).then((response) => {
dispatch({ type: types.AUTH_USER })
history.push('/cart')
})
}
}
I struggled with the same topic.
I'm using react-router-dom 5, Redux 4 and BrowserRouter.
I prefer function based components and hooks.
You define your component like this
import { useHistory } from "react-router-dom";
import { useDispatch } from "react-redux";
const Component = () => {
...
const history = useHistory();
dispatch(myActionCreator(otherValues, history));
};
And your action creator is following
const myActionCreator = (otherValues, history) => async (dispatch) => {
...
history.push("/path");
}
You can of course have simpler action creator if async is not needed
Here's my hack (this is my root-level file, with a little redux mixed in there - though I'm not using react-router-redux):
const store = configureStore()
const customHistory = createBrowserHistory({
basename: config.urlBasename || ''
})
ReactDOM.render(
<Provider store={store}>
<Router history={customHistory}>
<Route component={({history}) => {
window.appHistory = history
return (
<App />
)
}}/>
</Router>
</Provider>,
document.getElementById('root')
)
I can then use window.appHistory.push() anywhere I want (for example, in my redux store functions/thunks/sagas, etc) I had hoped I could just use window.customHistory.push() but for some reason react-router never seemed to update even though the url changed. But this way I have the EXACT instance react-router uses. I don't love putting stuff in the global scope, and this is one of the few things I'd do that with. But it's better than any other alternative I've seen IMO.
If you are using Redux, then I would recommend using npm package react-router-redux. It allows you to dispatch Redux store navigation actions.
You have to create store as described in their Readme file.
The easiest use case:
import { push } from 'react-router-redux'
this.props.dispatch(push('/second page'));
Second use case with Container/Component:
Container:
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import Form from '../components/Form';
const mapDispatchToProps = dispatch => ({
changeUrl: url => dispatch(push(url)),
});
export default connect(null, mapDispatchToProps)(Form);
Component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
export default class Form extends Component {
handleClick = () => {
this.props.changeUrl('/secondPage');
};
render() {
return (
<div>
<button onClick={this.handleClick}/>
</div>Readme file
);
}
}
I was able to accomplish this by using bind(). I wanted to click a button in index.jsx, post some data to the server, evaluate the response, and redirect to success.jsx. Here's how I worked that out...
index.jsx:
import React, { Component } from "react"
import { postData } from "../../scripts/request"
class Main extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.postData = postData.bind(this)
}
handleClick() {
const data = {
"first_name": "Test",
"last_name": "Guy",
"email": "test#test.com"
}
this.postData("person", data)
}
render() {
return (
<div className="Main">
<button onClick={this.handleClick}>Test Post</button>
</div>
)
}
}
export default Main
request.js:
import { post } from "./fetch"
export const postData = function(url, data) {
// post is a fetch() in another script...
post(url, data)
.then((result) => {
if (result.status === "ok") {
this.props.history.push("/success")
}
})
}
success.jsx:
import React from "react"
const Success = () => {
return (
<div className="Success">
Hey cool, got it.
</div>
)
}
export default Success
So by binding this to postData in index.jsx, I was able to access this.props.history in request.js... then I can reuse this function in different components, just have to make sure I remember to include this.postData = postData.bind(this) in the constructor().
so the way I do it is:
- instead of redirecting using history.push, I just use Redirect component from react-router-dom
When using this component you can just pass push=true, and it will take care of the rest
import * as React from 'react';
import { Redirect } from 'react-router-dom';
class Example extends React.Component {
componentDidMount() {
this.setState({
redirectTo: '/test/path'
});
}
render() {
const { redirectTo } = this.state;
return <Redirect to={{pathname: redirectTo}} push={true}/>
}
}
Use Callback. It worked for me!
export function addProduct(props, callback) {
return dispatch =>
axios.post(`${ROOT_URL}/cart`, props, config)
.then(response => {
dispatch({ type: types.AUTH_USER });
localStorage.setItem('token', response.data.token);
callback();
});
}
In component, you just have to add the callback
this.props.addProduct(props, () => this.props.history.push('/cart'))
React router V4 now allows the history prop to be used as below:
this.props.history.push("/dummy",value)
The value then can be accessed wherever the location prop is available as
state:{value} not component state.
As we have a history already included in react router 5, we can access the same with reference
import React from 'react';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
function App() {
const routerRef = React.useRef();
const onProductNav = () => {
const history = routerRef.current.history;
history.push("product");
}
return (
<BrowserRouter ref={routerRef}>
<Switch>
<Route path="/product">
<ProductComponent />
</Route>
<Route path="/">
<HomeComponent />
</Route>
</Switch>
</BrowserRouter>
)
}
step one wrap your app in Router
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(<Router><App /></Router>, document.getElementById('root'));
Now my entire App will have access to BrowserRouter. Step two I import Route and then pass down those props. Probably in one of your main files.
import { Route } from "react-router-dom";
//lots of code here
//somewhere in my render function
<Route
exact
path="/" //put what your file path is here
render={props => (
<div>
<NameOfComponent
{...props} //this will pass down your match, history, location objects
/>
</div>
)}
/>
Now if I run console.log(this.props) in my component js file that I should get something that looks like this
{match: {…}, location: {…}, history: {…}, //other stuff }
Step 2 I can access the history object to change my location
//lots of code here relating to my whatever request I just ran delete, put so on
this.props.history.push("/") // then put in whatever url you want to go to
Also I'm just a coding bootcamp student, so I'm no expert, but I know you can also you use
window.location = "/" //wherever you want to go
Correct me if I'm wrong, but when I tested that out it reloaded the entire page which I thought defeated the entire point of using React.
Create a custom Router with its own browserHistory:
import React from 'react';
import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';
export const history = createBrowserHistory();
const ExtBrowserRouter = ({children}) => (
<Router history={history} >
{ children }
</Router>
);
export default ExtBrowserRouter
Next, on your Root where you define your Router, use the following:
import React from 'react';
import { /*BrowserRouter,*/ Route, Switch, Redirect } from 'react-router-dom';
//Use 'ExtBrowserRouter' instead of 'BrowserRouter'
import ExtBrowserRouter from './ExtBrowserRouter';
...
export default class Root extends React.Component {
render() {
return (
<Provider store={store}>
<ExtBrowserRouter>
<Switch>
...
<Route path="/login" component={Login} />
...
</Switch>
</ExtBrowserRouter>
</Provider>
)
}
}
Finally, import history where you need it and use it:
import { history } from '../routers/ExtBrowserRouter';
...
export function logout(){
clearTokens();
history.push('/login'); //WORKS AS EXPECTED!
return Promise.reject('Refresh token has expired');
}
you can use it like this as i do it for login and manny different things
class Login extends Component {
constructor(props){
super(props);
this.login=this.login.bind(this)
}
login(){
this.props.history.push('/dashboard');
}
render() {
return (
<div>
<button onClick={this.login}>login</login>
</div>
)
/*Step 1*/
myFunction(){ this.props.history.push("/home"); }
/**/
<button onClick={()=>this.myFunction()} className={'btn btn-primary'}>Go
Home</button>
If you want to use history while passing a function as a value to a Component's prop, with react-router 4 you can simply destructure the history prop in the render attribute of the <Route/> Component and then use history.push()
<Route path='/create' render={({history}) => (
<YourComponent
YourProp={() => {
this.YourClassMethod()
history.push('/')
}}>
</YourComponent>
)} />
Note: For this to work you should wrap React Router's BrowserRouter Component around your root component (eg. which might be in index.js)

How do I programaticly navigate to a route from an event handler in React Router 4.0?

I need to navigate to a route after an event is successful.
This seems to have changed since previous versions.
Previously we would do this:
import { browserHistory } from 'react-router';
...
handleClick(){
doSomething();
browserHistory.push('/some/path');
}
This example works in react-router and react-router-dom v4.0.0.
We have 3 components:
App.js - holds both Component 1 and Component 2
Component1.js - not wrapped in a Route and will always be rendered, but this will not have a reference of the "route props" - (history, location, match...)
Component2.js - rendered only if the route location match. Important thing to note that this component will be rendered with "route props"
To navigate programmatically, you can use react-router history object.
this.props.history.push('path');
This will work right off the bat for components rendered via Route, as these components will already have access to the route props (history). In our example this is Component2. However, for components that are not rendered via a Route (e.g. Component1), you would need to wrap it in withRouter to give you access to the history object.
App.js
import React, { Component } from 'react';
import { BrowserRouter, Route } from 'react-router-dom';
import Component1 from './Component1';
import Component2 from './Component2';
class App extends Component {
render() {
return (
<BrowserRouter>
<div>
<Component1 />
<Route path="/render1" render={() => <div>Rendered from Component1</div>} />
<Route path="/" component={Component2} />
<Route path="/render2" render={() => <div>Rendered from Component2</div>} />
</div>
</BrowserRouter>
)
}
}
export default App;
Component1.js
import React from 'react';
import { withRouter } from 'react-router';
class Component1 extends React.Component {
handleButtonClick() {
this.props.history.push('/render1');
}
render() {
return (
<div>
<h1>Component 1</h1>
<button onClick={this.handleButtonClick.bind(this)}>Component 1 Button</button>
</div>
);
}
}
const Component1WithRouter = withRouter(Component1);
export default Component1WithRouter;
For Component1, we wrapped it in withRouter, and then exported the returned wrapped object. Some gotcha, notice that in App.js, we still reference it as Component1 instead of Component1WithRouter
Component2.js
import React from 'react';
class Component2 extends React.Component {
handleButtonClick() {
this.props.history.push('/render2');
}
render() {
return (
<div>
<h1>Component 2</h1>
<button onClick={this.handleButtonClick.bind(this)}>Component 2 Button</button>
</div>
);
}
}
export default Component2;
For Component2, the history object is already available from this.props. You just need to invoke the push function.
If you are using "function components" and Hooks, instead of expecting props, use the useHistory() function instead:
import {useHistory} from 'react-router-dom';
export default function MyFunctionComponent() {
const history = useHistory();
const myEventHandler = () => {
// do stuff
history.push('/newpage');
};
// ...
}
Just render a redirect component:
import { Redirect } from 'react-router';
// ...
<Redirect to="/some/path" />
See docs here: https://reacttraining.com/react-router/core/api/Redirect
I really appreciate CodinCat's answer since he helped me resolve a different error, but I found a more correct solution:
In React Router 4.0 (I don't know about previous versions) the router passes a history object to the component (i.e.: this.props.history). You can push your url onto that array to redirect:
this.props.history.push('/dogs');
In my case though, I had two levels of components, the router called a component called LoginPage, and LoginPage called a component called Login. You'll only have the history object in the props of your child object if you pass it on:
<Router>
<Route path="/dogs" component={DogsPage}/>
<Route path="/login" component={LoginPage}/>
</Router>
const LoginPage = (props) => {
// Here I have access to props.history
return (
<div>
<Login history={props.history} />
</div>
)
}
const Login = (props) => {
function handleClick(){
// Now we can simply push our url onto the history and the browser will update
props.history.push('/dogs');
}
return (
<div>
<button onClick={handleClick}>Click to Navigate</button>
</div>
)
}
I know the example above is a bit contrived, since it would be easier in this case to just use a link, but I made the example this way to keep it concise. There are many reasons you may need to navigate this way. In my case, I was doing a graphQL request and wanted to navigate to the home page once a person had successfully logged in.

react-router go back a page how do you configure history?

Can anyone please tell me how I can go back to the previous page rather than a specific route?
When using this code:
var BackButton = React.createClass({
mixins: [Router.Navigation],
render: function() {
return (
<button
className="button icon-left"
onClick={this.navigateBack}>
Back
</button>
);
},
navigateBack: function(){
this.goBack();
}
});
Get this error, goBack() was ignored because there is no router history
Here are my routes:
// Routing Components
Route = Router.Route;
RouteHandler = Router.RouteHandler;
DefaultRoute = Router.DefaultRoute;
var routes = (
<Route name="app" path="/" handler={OurSchoolsApp}>
<DefaultRoute name="home" handler={HomePage} />
<Route name="add-school" handler={AddSchoolPage} />
<Route name="calendar" handler={CalendarPage} />
<Route name="calendar-detail" path="calendar-detail/:id" handler={CalendarDetailPage} />
<Route name="info-detail" path="info-detail/:id" handler={InfoDetailPage} />
<Route name="info" handler={InfoPage} />
<Route name="news" handler={NewsListPage} />
<Route name="news-detail" path="news-detail/:id" handler={NewsDetailPage} />
<Route name="contacts" handler={ContactPage} />
<Route name="contact-detail" handler={ContactDetailPage} />
<Route name="settings" handler={SettingsPage} />
</Route>
);
Router.run(routes, function(Handler){
var mountNode = document.getElementById('app');
React.render(<Handler /> , mountNode);
});
Update with React v16 and ReactRouter v4.2.0 (October 2017):
class BackButton extends Component {
static contextTypes = {
router: () => true, // replace with PropTypes.object if you use them
}
render() {
return (
<button
className="button icon-left"
onClick={this.context.router.history.goBack}>
Back
</button>
)
}
}
Update with React v15 and ReactRouter v3.0.0 (August 2016):
var browserHistory = ReactRouter.browserHistory;
var BackButton = React.createClass({
render: function() {
return (
<button
className="button icon-left"
onClick={browserHistory.goBack}>
Back
</button>
);
}
});
Created a fiddle with a little bit more complex example with an embedded iframe: https://jsfiddle.net/kwg1da3a/
React v14 and ReacRouter v1.0.0 (Sep 10, 2015)
You can do this:
var React = require("react");
var Router = require("react-router");
var SomePage = React.createClass({
...
contextTypes: {
router: React.PropTypes.func
},
...
handleClose: function () {
if (Router.History.length > 1) {
// this will take you back if there is history
Router.History.back();
} else {
// this will take you to the parent route if there is no history,
// but unfortunately also add it as a new route
var currentRoutes = this.context.router.getCurrentRoutes();
var routeName = currentRoutes[currentRoutes.length - 2].name;
this.context.router.transitionTo(routeName);
}
},
...
You need to be careful that you have the necessary history to go back. If you hit the page directly and then hit back it will take you back in the browser history before your app.
This solution will take care of both scenarios. It will, however, not handle an iframe that can navigate within the page (and add to the browser history), with the back button. Frankly, I think that is a bug in the react-router. Issue created here: https://github.com/rackt/react-router/issues/1874
Using React Hooks
Import:
import { useHistory } from "react-router-dom";
In stateless component:
let history = useHistory();
Call the Event:
history.goBack()
Examples do use in event Button:
<button onClick={history.goBack}>Back</button>
or
<button onClick={() => history.goBack()}>Back</button>
import withRouter
import { withRouter } from 'react-router-dom';
Export your component as:
export withRouter(nameofcomponent)
Example, on button click, call goBack:
<button onClick={this.props.history.goBack}>Back</button>
Tested on react-router-dom v4.3
I think you just need to enable BrowserHistory on your router by intializing it like that : <Router history={new BrowserHistory}>.
Before that, you should require BrowserHistory from 'react-router/lib/BrowserHistory'
Here's an example using ES6
const BrowserHistory = require('react-router/lib/BrowserHistory').default;
const App = React.createClass({
render: () => {
return (
<div><button onClick={BrowserHistory.goBack}>Go Back</button></div>
);
}
});
React.render((
<Router history={BrowserHistory}>
<Route path="/" component={App} />
</Router>
), document.body);
React Router v6
useNavigate Hook is the recommended way to go back now:
import { useNavigate } from 'react-router-dom';
function App() {
const navigate = useNavigate();
return (
<>
<button onClick={() => navigate(-1)}>go back</button>
<button onClick={() => navigate(1)}>go forward</button>
</>
);
}
Codesandbox sample
Go back/forward multiple history stack entries:
<button onClick={() => navigate(-2)}>go two back</button>
<button onClick={() => navigate(2)}>go two forward</button>
Go to specific route:
navigate("users") // go to users route, like history.push
navigate("users", { replace: true }) // go to users route, like history.replace
navigate("users", { state }) // go to users route, pass some state in
useNavigate replaces useHistory to support upcoming React Suspense/Concurrent mode better.
this.context.router.goBack()
No navigation mixin required!
ES6 method without mixins using react-router, stateless function.
import React from 'react'
import { browserHistory } from 'react-router'
export const Test = () => (
<div className="">
<button onClick={browserHistory.goBack}>Back</button>
</div>
)
Go back to specific page:
import { useHistory } from "react-router-dom";
const history = useHistory();
const routeChange = () => {
let path = '/login';
history.push(path);
};
Go back to previous page:
import { useHistory } from "react-router-dom";
const history = useHistory();
const routeChange = () => {
history.goBack()
};
Check out my working example using React 16.0 with React-router v4. check out the code Github
Use withRouter and history.goBack()
This is the idea I am implementing...
History.js
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom'
import './App.css'
class History extends Component {
handleBack = () => {
this.props.history.goBack()
}
handleForward = () => {
console.log(this.props.history)
this.props.history.go(+1)
}
render() {
return <div className="container">
<div className="row d-flex justify-content-between">
<span onClick={this.handleBack} className="d-flex justify-content-start button">
<i className="fas fa-arrow-alt-circle-left fa-5x"></i>
</span>
<span onClick={this.handleForward} className="d-flex justify-content-end button">
<i className="fas fa-arrow-alt-circle-right fa-5x"></i>
</span>
</div>
</div>
}
}
export default withRouter(History)
PageOne.js
import React, { Fragment, Component } from 'react'
class PageOne extends Component {
componentDidMount(){
if(this.props.location.state && this.props.location.state.from != '/pageone')
this.props.history.push({
pathname: '/pageone',
state: {
from: this.props.location.pathname
}
});
}
render() {
return (
<Fragment>
<div className="container-fluid">
<div className="row d-flex justify-content-center">
<h2>Page One</h2>
</div>
</div>
</Fragment>
)
}
}
export default PageOne
p.s. sorry the code is to big to post it all here
This works with Browser and Hash history.
this.props.history.goBack();
This is a working BackButton component (React 0.14):
var React = require('react');
var Router = require('react-router');
var History = Router.History;
var BackButton = React.createClass({
mixins: [ History ],
render: function() {
return (
<button className="back" onClick={this.history.goBack}>{this.props.children}</button>
);
}
});
module.exports = BackButton;
You can off course do something like this if there is no history:
<button className="back" onClick={goBack}>{this.props.children}</button>
function goBack(e) {
if (/* no history */) {
e.preventDefault();
} else {
this.history.goBack();
}
}
For react-router v2.x this has changed. Here's what I'm doing for ES6:
import React from 'react';
import FontAwesome from 'react-fontawesome';
import { Router, RouterContext, Link, browserHistory } from 'react-router';
export default class Header extends React.Component {
render() {
return (
<div id="header">
<div className="header-left">
{
this.props.hasBackButton &&
<FontAwesome name="angle-left" className="back-button" onClick={this.context.router.goBack} />
}
</div>
<div>{this.props.title}</div>
</div>
)
}
}
Header.contextTypes = {
router: React.PropTypes.object
};
Header.defaultProps = {
hasBackButton: true
};
Header.propTypes = {
title: React.PropTypes.string
};
In react-router v4.x you can use history.goBack which is equivalent to history.go(-1).
App.js
import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import Home from "./Home";
import About from "./About";
import Contact from "./Contact";
import Back from "./Back";
const styles = {
fontFamily: "sans-serif",
textAlign: "left"
};
const App = () => (
<div style={styles}>
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
<Back />{/* <----- This is component that will render Back button */}
</div>
</Router>
</div>
);
render(<App />, document.getElementById("root"));
Back.js
import React from "react";
import { withRouter } from "react-router-dom";
const Back = ({ history }) => (
<button onClick={history.goBack}>Back to previous page</button>
);
export default withRouter(Back);
Demo:
https://codesandbox.io/s/ywmvp95wpj
Please remember that by using history your users can leave because history.goBack() can load a page that visitor has visited before opening your application.
To prevent such situation as described above, I've created a simple library react-router-last-location that watch your users last location.
Usage is very straight forward.
First you need to install react-router-dom and react-router-last-location from npm.
npm install react-router-dom react-router-last-location --save
Then use LastLocationProvider as below:
App.js
import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { LastLocationProvider } from "react-router-last-location";
// ↑
// |
// |
//
// Import provider
//
import Home from "./Home";
import About from "./About";
import Contact from "./Contact";
import Back from "./Back";
const styles = {
fontFamily: "sans-serif",
textAlign: "left"
};
const App = () => (
<div style={styles}>
<h5>Click on About to see your last location</h5>
<Router>
<LastLocationProvider>{/* <---- Put provider inside <Router> */}
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
<hr />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
<Back />
</div>
</LastLocationProvider>
</Router>
</div>
);
render(<App />, document.getElementById("root"));
Back.js
import React from "react";
import { Link } from "react-router-dom";
import { withLastLocation } from "react-router-last-location";
// ↑
// |
// |
//
// `withLastLocation` higher order component
// will pass `lastLocation` to your component
//
// |
// |
// ↓
const Back = ({ lastLocation }) => (
lastLocation && <Link to={lastLocation || '/'}>Back to previous page</Link>
);
// Remember to wrap
// your component before exporting
//
// |
// |
// ↓
export default withLastLocation(Back);
Demo: https://codesandbox.io/s/727nqm99jj
I want to update the previous answers a bit.
If you are using react-router >v6.0 then the useHistory() is not the right way to go back. You will get an error as I guess useHistory() is not present in the latest version.
So this is the updated answer
// This is a React Router v6 app
import { useNavigate } from "react-router-dom";
function App() {
const navigate = useNavigate();
return (
<>
<button onClick={() => navigate(-2)}>
Go 2 pages back
</button>
<button onClick={() => navigate(-1)}>Go back</button>
<button onClick={() => navigate(1)}>
Go forward
</button>
<button onClick={() => navigate(2)}>
Go 2 pages forward
</button>
</>
);
}
Use this useNavigate() hook.
You can read the official doc for this transition from v5 to v6 here
https://reactrouter.com/docs/en/v6/upgrading/v5
What worked for me was to import withRouter at the top of my file;
import { withRouter } from 'react-router-dom'
Then use it to wrap the exported function at the bottom of my file;
export default withRouter(WebSitePageTitleComponent)
Which then allowed me to access the Router's history prop. Full sample code below!
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
import PropTypes from 'prop-types'
class TestComponent extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
event.preventDefault()
this.props.history.goBack()
}
render() {
return (
<div className="page-title">
<a className="container" href="/location" onClick={this.handleClick}>
<h1 className="page-header">
{ this.props.title }
</h1>
</a>
</div>
)
}
}
const { string, object } = PropTypes
TestComponent.propTypes = {
title: string.isRequired,
history: object
}
export default withRouter(TestComponent)
On react-router-dom v6
import { useNavigate } from 'react-router-dom';
function goBack() {
const navigate = useNavigate();
return <button onClick={() => navigate(-1)}>go back</button>
}
import { withRouter } from 'react-router-dom'
this.props.history.goBack();
I am using these versions
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
Step-1
import { useHistory } from "react-router-dom";`
Step-2
let history = useHistory();
Step-3
const goToPreviousPath = (e) => {
e.preventDefault();
history.goBack()
}
step-4
<Button
onClick={goToPreviousPath}
>
Back
</Button>
REDUX
You can also use react-router-redux which has goBack() and push().
Here is a sampler pack for that:
In your app's entry point, you need ConnectedRouter, and a sometimes tricky connection to hook up is the history object. The Redux middleware listens to history changes:
import React from 'react'
import { render } from 'react-dom'
import { ApolloProvider } from 'react-apollo'
import { Provider } from 'react-redux'
import { ConnectedRouter } from 'react-router-redux'
import client from './components/apolloClient'
import store, { history } from './store'
import Routes from './Routes'
import './index.css'
render(
<ApolloProvider client={client}>
<Provider store={store}>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</Provider>
</ApolloProvider>,
document.getElementById('root'),
)
I will show you a way to hook up the history. Notice how the history is imported into the store and also exported as a singleton so it can be used in the app's entry point:
import { createStore, applyMiddleware, compose } from 'redux'
import { routerMiddleware } from 'react-router-redux'
import thunk from 'redux-thunk'
import createHistory from 'history/createBrowserHistory'
import rootReducer from './reducers'
export const history = createHistory()
const initialState = {}
const enhancers = []
const middleware = [thunk, routerMiddleware(history)]
if (process.env.NODE_ENV === 'development') {
const { devToolsExtension } = window
if (typeof devToolsExtension === 'function') {
enhancers.push(devToolsExtension())
}
}
const composedEnhancers = compose(applyMiddleware(...middleware), ...enhancers)
const store = createStore(rootReducer, initialState, composedEnhancers)
export default store
The above example block shows how to load the react-router-redux middleware helpers which complete the setup process.
I think this next part is completely extra, but I will include it just in case someone in the future finds benefit:
import { combineReducers } from 'redux'
import { routerReducer as routing } from 'react-router-redux'
export default combineReducers({
routing, form,
})
I use routerReducer all the time because it allows me to force reload Components that normally do not due to shouldComponentUpdate. The obvious example is when you have a Nav Bar that is supposed to update when a user presses a NavLink button. If you go down that road, you will learn that Redux's connect method uses shouldComponentUpdate. With routerReducer, you can use mapStateToProps to map routing changes into the Nav Bar, and this will trigger it to update when the history object changes.
Like this:
const mapStateToProps = ({ routing }) => ({ routing })
export default connect(mapStateToProps)(Nav)
Forgive me while I add some extra keywords for people: if your component isn't updating properly, investigate shouldComponentUpdate by removing the connect function and see if it fixes the problem. If so, pull in the routerReducer and the component will update properly when the URL changes.
In closing, after doing all that, you can call goBack() or push() anytime you want!
Try it now in some random component:
Import in connect()
You don't even need mapStateToProps or mapDispatchToProps
Import in goBack and push from react-router-redux
Call this.props.dispatch(goBack())
Call this.props.dispatch(push('/sandwich'))
Experience positive emotion
If you need more sampling, check out: https://www.npmjs.com/package/react-router-redux
Simply use like this
<span onClick={() => this.props.history.goBack()}>Back</span>
The only solution that worked for me was the most simple. No additional imports needed.
<a href="#" onClick={() => this.props.history.goBack()}>Back</a>
Tks, IamMHussain
React Router uses the HTML5 History API, which builds on the browser history API to provide an interface to which we can use easily in React apps. History API . So without import anything (useHistory, etc)
for functional component:
<button onClick={()=>{ window.history.back() }}> Back </button>
for class component:
<button onClick={()=>{ this.window.history.back() }}> Back </button>
In react-router v6, when you want to go back to the previous page, you can do that with useNavigate:
Step 1:
import { useNavigate } from "react-router-dom";
Step2:
const navigate = useNavigate();
Step 3: if you want to go back to the previous page, use navigate(-1):
<button onClick={() => navigate(-1)}> Back </button>
If you are using react-router v6, when you want to go back to the previous page you can do that with the Link:
Step 1: you need to import Link from react-router-dom
import { Link } from 'react-router-dom';
Step 2: wrap up the button with Link like this. It works perfectly.
<Link to='..'>
<Button type='button'>Go Back</Button>
</Link>
Call the following component like so:
<BackButton history={this.props.history} />
And here is the component:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
class BackButton extends Component {
constructor() {
super(...arguments)
this.goBack = this.goBack.bind(this)
}
render() {
return (
<button
onClick={this.goBack}>
Back
</button>
)
}
goBack() {
this.props.history.goBack()
}
}
BackButton.propTypes = {
history: PropTypes.object,
}
export default BackButton
I'm using:
"react": "15.6.1"
"react-router": "4.2.0"
if you are using react-native drawer navigation as main router in your application and want to control back button behavior and go back historically you can use to control back button.
<NavigationContainer>
<Drawer.Navigator
backBehavior="history">
// your screens come here
</Drawer.Navigator>
</NavigationContainer>
This piece of code will do the trick for you.
this.context.router.history.goBack()
According to https://reacttraining.com/react-router/web/api/history
For "react-router-dom": "^5.1.2",,
const { history } = this.props;
<Button onClick={history.goBack}>
Back
</Button>
YourComponent.propTypes = {
history: PropTypes.shape({
goBack: PropTypes.func.isRequired,
}).isRequired,
};

Categories

Resources