Manzurul Hoque Rumi
Manzurul Hoque Rumi

Reputation: 3094

React-router TypeError: _this.props.history is undefined

I am using react-router with react js and i following their documentation but facing this error

while compiling it shows the error,

TypeError: _this.props.history is undefined

this is my index.js file

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import { Router, Route, browserHistory, IndexRoute } from 'react-router';

ReactDOM.render(
  <Router history={browserHistory}>
    <Route path="/" component={App}>

    </Route>
  </Router>
  ,
  document.getElementById('root')
);

and this is my App.js file

import React, { Component } from 'react';
import './App.css';

class App extends Component {
    constructor(props){
        super(props);

        this.state = {
            headerText: "Props from Header.",
            contentText: "Props from content."
        };
    }
    render() {
        return (
          <div className="App">
            <ul>
                <li><a href="">Home</a></li>
                <li><a href="">Home</a></li>
                <li><a href="">Home</a></li>
            </ul>
          </div>
        );
    }
}

export default App;

Upvotes: 47

Views: 90683

Answers (14)

ttemple
ttemple

Reputation: 1985

I was getting this issue because I was trying to use props.history.push() with RRD v6, but v6 has scrapped the useHistory hook and instead uses the useNavigate hook.

For example:

import {useNavigate} from 'react-router-dom';
const navigate = useNavigate();
navigate('/path');

See this link for more info: https://reactrouter.com/en/main/hooks/use-navigate

Upvotes: 0

akshay_sushir
akshay_sushir

Reputation: 1841

I have problems with my Form component.

this.props.history.push('/') is undefined.

to solve this i've added import {withRouter} from 'react-router-dom' And then export default component as: export default withRouter(connect(mapStateToProps)(CustomForm));

import React from "react";
import { Form, Input, Button } from "antd";
import { connect } from "react-redux";
import {withRouter} from 'react-router-dom'
import axios from "axios";

const FormItem = Form.Item;


class CustomForm extends React.Component {
  
  handleFormSubmit = async (event, requestType, articleID, ) => {
    event.preventDefault();

const postObj = {
  title: event.target.elements.title.value,
  content: event.target.elements.content.value
}

axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.headers = {
  "Content-Type": "application/json",
  Authorization: `Token ${this.props.token}`,
};

if (requestType === "post") {
  await axios.post("http://127.0.0.1:8000/api/create/", postObj)
    .then(res => {
      if (res.status === 201) {
        this.props.history.push(`/`);
      }
    })
} else if (requestType === "put") {
  await axios.put(`http://127.0.0.1:8000/api/${articleID}/update/`, postObj)
    .then(res => {
      if (res.status === 200) {
        this.props.history.push(`/`);
      }
    })
}


};

  render() {
    return (
      <div>
        <Form
          onSubmit={event =>
            this.handleFormSubmit(
              event,
              this.props.requestType,
              this.props.articleID
            )
          }
        >
          <FormItem label="Title">
            <Input name="title" placeholder="Put a title here" />
          </FormItem>
          <FormItem label="Content">
            <Input name="content" placeholder="Enter some content ..." />
          </FormItem>
          <FormItem>
            <Button type="primary" htmlType="submit">
              {this.props.btnText}
            </Button>
          </FormItem>
        </Form>
      </div>
    );
  }
}

const mapStateToProps = state => {
  return {
    token: state.token
  };
};

export default withRouter(connect(mapStateToProps)(CustomForm));

I hope this is useful for someone. I am using Django as a backend

Upvotes: 7

Sanan Ali
Sanan Ali

Reputation: 3427

I was trying to use history in the functional component and it was undefined because I was trying to access it with props. According to the docs, we need to use useHistory hook to access the history. Here is the sample code according to the docs.

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

You can check this link for more understanding.

Upvotes: 4

Suraj
Suraj

Reputation: 894

This works for me >>>

import { Router, Route, browserHistory, IndexRoute } from 'react-router';

change the 'react-router' with 'react-router-dom', and Router with BrowserRouter as Router.

Like this >>>

import { BrowserRouter as Router, Route, browserHistory, IndexRoute } from 'react-router-dom';

Upvotes: 3

Mebatsion Sahle
Mebatsion Sahle

Reputation: 429

Check that if there are any components that you haven't linked to router or any links that is not necessary among your components.

Besides if you have <Router><Link>...</Link></Router> in one of your clickable components it might crash your work.

Hope it might help.

Upvotes: 0

Mahdieh Shavandi
Mahdieh Shavandi

Reputation: 8635

I had something like:

<Router>
   <HeaderComponent/>
   <Router exact path="/" component={Home}/>
   <Router path="/auth" component={AuthLayout}/>
   <FooterComponent/>
 </Router>

and AuthLayout was a component in which I was switching between SignIn and SignUp components, like below:

<div>
  { login ? <SignIn/> : <SignUp/> }
</div>

I faced this error of this.props.history is not a function inside these components. It was because I hadn't used those components directly inside the router. I had access to this.props.history inside the AuthLayout and I had to pass it to its children.

So I did it:

<div>
  { login ? <SignIn history={this.props.history}/> :  <SignUp history={this.props.history}/> }
</div>

and the problem solved.

Upvotes: 2

Walkance
Walkance

Reputation: 392

For me the solution was to change

1) component as child

<Route path="/path">
 <MyComponent/>
</Route>

to

2) component as the "component" prop

<Route path="/path" component={MyComponent}>
</Route>

In both ways it renders, so it was very confusing for me, in examples they provide code as in first example. (I used the version 5.1.2 of the "react-router-dom" package at the moment).

Update

You can also use this way (for child components (children of "MyComponent") works only this one)

import {withRouter} from 'react-router-dom';

const componentClassWithHistory = withRouter(ChildComponent);
export {componentClassWithHistory as ChildComponent};

or for default export

export default withRouter(ChildComponent)

or for typescript

const componentClassWithHistory = (withRouter(ChildComponent as any) as any);
export {componentClassWithHistory as ChildComponent};

Source: https://reacttraining.com/react-router/core/api/withRouter

Hope it helps someone.

Upvotes: 16

elonaire
elonaire

Reputation: 1994

This should be done in the component that is being routed to. In this case, it is App component. Therefore, in App.js, import "createBrowserHistory" and do it as follows:

import React, { Component } from 'react';
import './App.css';
import { createBrowserHistory } from "history";

class App extends Component {
    constructor(props){
        super(props);

        this.history = createBrowserHistory();;

        this.state = {
            headerText: "Props from Header.",
            contentText: "Props from content."
        };
    }
    render() {
        return (
          <div className="App">
            <ul>
                <li><a href="">Home</a></li>
                <li><a href="">Home</a></li>
                <li><a href="">Home</a></li>
            </ul>
          </div>
        );
    }
}

export default App;

Upvotes: 0

Benyam
Benyam

Reputation: 399

you can use window.location.href = '/somePath' as your last resort

Upvotes: 5

Ian Greenleaf Young
Ian Greenleaf Young

Reputation: 1958

I encountered this problem when trying to instantiate my own Router rather than using one of the built-in ones, to deal with an issue of having access to the history:

history.js

import { createHashHistory as createHistory } from 'history'

export default createHistory()

root.jsx

import { Router, Route } from 'react-router-dom'
import history from './history'

...

  <Router history={history}>
    <Route path="/test" component={Test}/>
  </Router>

In my case, the trouble was that I had a version of the history package locked at v2.1.2, while react-router was depending on a v4.x version of history in its own dependencies. So the built-in routers instantiated fine using the newer history, but when I tried to do it myself I was pulling in the older history and getting an incompatible object.

Upvotes: 2

Ramiro Farto
Ramiro Farto

Reputation: 73

It seems to me that this.props.history is not defined, because you did not pass the routeProps to the component you want to render.

Change your routes as follows:

 <Route path="/home" render={(routeProps) => <Home {...routeProps}/>} />

You can also pass other props to the rendered components using this method.

Upvotes: 4

Amish Shabani
Amish Shabani

Reputation: 749

The below solution works for me in ReactDOM.render:

 <BrowserRouter>
  <Switch>
    <Route path="/home" component={Home} />
    <Route path="/user" component={User} />
    <Route path="*" component={page404} />
  </Switch>
 </BrowserRouter>

It use multiple Routing.

Upvotes: 0

Guillaume
Guillaume

Reputation: 10961

This is because things changed in React Router starting with 4.0.0. You should now use BrowserRouter from react-router-dom package when you were using browserHistory. So your code will looks like:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import { BrowserRouter, Route } from 'react-router-dom';

ReactDOM.render(
    <BrowserRouter>
        <Route path="/" component={ App }/>
    </BrowserRouter>, document.getElementById('root')
);

Of course, you'll need to install react-router-dom first.

Also note that if you're using more than one Route element, you'll have to use a Switch as explained in this answer.

Upvotes: 39

Andrew Pelykh
Andrew Pelykh

Reputation: 115

Are you using npm? I had the same problem with "react-router": "^4.0.0" in my package.json. Changing it to "react-router": "^3.0.2" solved my problem.

Upvotes: 7

Related Questions