9

I have this code. What I want to do is when I click a button 'feature' it will take me to index route. However, React keeps saying 'can not read property push of undefined' What I've done wrong?

route.js

import React from "react";
import ReactDOM from "react-dom";
import {Router, Route, hashHistory, IndexRoute } from "react-router";

import Layout from "./page/Layout";
import Features from "./page/Features";
import Features from "./page/archive";

const app = document.getElementById('app');

ReactDOM.render(
    <Router history={hashHistory}>
        <Route path="/" component={Layout}>
            <IndexRoute component={Features} />
            <Route path="archive" component={Archive} />
        </Route>
    </Router>, app);

Layout component

import React from "react";
import {Link, Router, Route, hashHistory} from "react-router";
export default class Layout extends React.Component{
    navigate(){
        this.context.router.push('/');
    }
    render(){ 
        return(
            <div>
                {this.props.children}
                <button onClick={this.navigate.bind(this)}>feature</button>
            </div>
        )
    }
}

package.json - partial

"react": "^0.14.7",
"react-dom": "^0.14.7",
"react-router": "^2.0.1"
 "history": "^2.0.1",

-------------update to jordan's answer-------------

angry kiwi
  • 10,730
  • 26
  • 115
  • 161

11 Answers11

17

In React Router v4, you no longer have to give a history to your router. Instead you just use BrowserRouter or HashRouter from 'react-router-dom'. But that makes it unclear how to push a rout to your history when you aren't in a react component.

The solution is to use the history package.

Just import createHistory like this:

import createHistory from 'history/createBrowserHistory'

Or the way I do it is like this:

import { createHashHistory } from 'history'

then create your history

export const history = createHashHistory()

and now you can push to it:

history.push('/page')

I hope this helps others who come to this question. None of the current answers gave me what I needed.

Wylliam Judd
  • 9,935
  • 2
  • 26
  • 36
11

This may not be referring to above example but I had the same error. After lot of debugging I figured out that history is not reachable to my inner components. Make sure your history is reachable.

//main.js

<BrowserRouter>
    <div>
        <Route path="/" component={Home}/>
        <Route path="/techMap" component={TechMap}/>
    </div>
 </BrowserRouter>

//app.js

<div>
   <TechStack history= {this.props.history}/>
</div>

//techstack.js

<div>
    <span onClick={this.search.bind(this)}>
    </span>
 </div>
)

search(e){
 this.props.history.push('/some_url');
}

TechStack is my inner component.

Earlier I was able to get history in app.js but not in tech.js. But after passing props in form of history, I got the history in tech.js and routing works

Prayag
  • 331
  • 2
  • 9
7

With React router v4, you need to wrap the components in withRouter. Then you can access history in your components. Do the following:

import { withRouter } from 'react-router-dom';
...
...
export default withRouter(MyComponent);
Peter
  • 10,492
  • 21
  • 82
  • 132
  • How would the export statement look if you were using Redux as well. Would it be export default connect(mapStateToProps, null)(withRouter(App)) or the other way around? Thanks! – Sreehari R Apr 07 '20 at 06:34
4

You need to change your route.js page to

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

ReactDOM.render(
    <Router history={browserHistory}>
        <Route path="/" component={Layout}>
            <IndexRoute component={Features} />
            <Route path="archive" component={Archive} />
        </Route>
    </Router>, app);

And then everywhere you want to navigate you can use

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

 browserHistory.push('/');

The react-router docs encourage you to use browserHistory instead of hashHistory

hashHistory uses URL hashes, along with a query key to keep track of state. hashHistory requires no additional server configuration, but is generally less preferred than browserHistory.

Aaleks
  • 4,283
  • 5
  • 31
  • 39
3

usually, when you are trying to redirect from a nested component it will give this error.

there are a few ways to fix it

Using react-dom you can import the withRouter component from react-router-dom then use it as usual with this.props.history.push and instead of the usual export default 'class' we will use export default withRouter(class); and boom problem solve.

jerryurenaa
  • 3,863
  • 1
  • 27
  • 17
2

I use browserHistory instead of HashHistory. Then I just need to do the following:

import { browserHistory } from 'react-router'
// ...
// ...

   navigate(){
        browserHistory.push('/');
    }
hellogoodnight
  • 1,989
  • 7
  • 29
  • 57
1

You don't need to use browserHistory anymore. React-router-dom inject into your component route related props and context. One of this props is 'history' and on this history object is a function push that you can call and pass the route you want to navigate to.

example in a Class base component, you can create a function like below as an onClick handler to redirect to specific link

redirectToPage() {
 this.props.history.push('/page'); OR
 this.context.router.history.push('/page');
}

while in a function base stateless component

redirectToSessionStatePage() {
 props.history.push('/page');OR
 context.router.history.push('/page');
}
0

Change your Layout component to have navigate assigned to ES6 lambda. This is needed to set the correct value of this

import React from "react";
import {Link, Router, Route, hashHistory} from "react-router";
export default class Layout extends React.Component{
    navigate = () => {
        this.context.router.push('/');
    }

    render(){ 
      return(
        <div>
            {this.props.children}
            <button onClick={this.navigate.bind(this)}>feature</button>
        </div>
      )
    }
}
Aditya Singh
  • 15,810
  • 15
  • 45
  • 67
0
export default class Layout extends React.Component{

    navigate = () => {
        this.context.router.push('/');
    }
    render(){ 
        return(
            <div>
                {this.props.children}
                <button onClick={this.navigate.bind(this)}>feature</button>
            </div>
        )
    }
}
Layout.contextTypes = {
    router: PropTypes.object.isRequired
}
angry kiwi
  • 10,730
  • 26
  • 115
  • 161
0

It looks like you overwrote your Features import with whatever is in your /archives directory. In the code you posted, you have this:

import Features from "./page/Features";
import Features from "./page/archive";
doubledherin
  • 340
  • 1
  • 3
  • 7
0

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

export default withRouter(AppName);

  • While this code snippet may solve the question, [including an explanation](//s.tk/meta/questions/114762/explaining-entirely-code-based-answers) really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion. Please also try not to crowd your code with explanatory comments, this reduces the readability of both the code and the explanations! – Filnor Apr 30 '20 at 13:18