7

I'm trying to modify the example code of react-router-redux. https://github.com/rackt/react-router-redux/blob/master/examples/basic/components/Home.js

this is my Home.js

class Home extends Component {
    onSubmit(props) {
        this.props.routeActions.push('/foo');    
    }
}

I also have a mapDispatchToProps for it.

function mapDispatchToProps(dispatch){
    return bindActionCreators({ routeActions },dispatch);
}

When i called onSubmit function, I got an error

Uncaught TypeError: this.props.routeActions.push is not a function

If i remove this.props in onSubmit, the key in the URL changed but its still on the same page. From localhost:8080/#/?_k=iwio19 to localhost:8080/#/?_k=ldn1ew

Anyone know how to fix it? Appreciate it.

cchamberlain
  • 14,693
  • 6
  • 52
  • 67
JAckWang
  • 73
  • 1
  • 1
  • 4

5 Answers5

9

I don't think routeActions are passed as props. What you want to do is this:

import { routeActions } from 'react-router-redux'

this.props.dispatch(routeActions.push('/foo'));
Fabio
  • 1,519
  • 17
  • 32
jjordy
  • 146
  • 5
  • 1
    This is indeed how you should do it. The problem comes up when you mapDispatchToProps and still want to use dispatch. for example connect(null, { someAction })(YourComponent) will remove dispatch from props. Any idea how to use routeActions in this scenario? – Michiel Feb 05 '16 at 14:06
  • I'm sorry I didn't catch that the first time. What if you moved your route action to your actions file and called the route function from there. That way it wouldn't interfere with mapDispatchToProps and you could reuse the action in multiple components – jjordy Feb 08 '16 at 15:04
9

To provide a little more explicit answer and the answer to my own question in the comment.

Yes you can do

import { routeActions } from 'react-router-redux'

this.props.dispatch(routeActions.push('/foo));

However as I mentioned mapDispatchToProps will override this. To fix this you can bind the routeActions like so:

import { bindActionCreators } from 'redux'
import { routeActions } from 'react-router-redux'

function mapDispatchToProps(dispatch) {
    return {
        routeActions: bindActionCreators(routeActions, dispatch),
    }
}

export default connect(null, mapDispatchToProps)(YourComponent)

Now you can do: this.props.routeActions.push('/foo')

Just FYI this can be done even neater

function mapDispatchToProps(dispatch) {
    return {
        ...bindActions({routeActions, anotherAction}, dispatch)
    }
}
schneck
  • 8,948
  • 11
  • 43
  • 69
Michiel
  • 1,041
  • 10
  • 17
  • 1
    I think routeActions is no longer part of react router redux. Can you please confirm ? – agpt Dec 01 '16 at 11:01
  • 2
    It says, `Cannot read property 'push' of undefined(…)` ,where as import {push} is working fine – agpt Dec 01 '16 at 11:13
7

As for react-router-redux v4:

import { push } from 'react-router-redux';

export class ExampleContainer extends React.Component {
  static propTypes = {
    changeRoute: React.PropTypes.func,
  };

  function mapDispatchToProps(dispatch) {
    return {
      changeRoute: (url) => dispatch(push(url)),
      dispatch,
    };
  }
}

export default connect(null, mapDispatchToProps)(ExampleContainer);

then:

this.props.changeRoute('/foo');
quotesBro
  • 4,240
  • 2
  • 27
  • 38
  • 4
    Please @Mikhail how do I navigate programmatically if I am using browserHistory in my Router component? I just tried your solution but it didn't work as expected. – cdaiga Dec 22 '16 at 09:06
5

I was able to get it working by doing this

import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { routeActions } from 'react-router-redux'

export const Cmpt = React.createClass({ //code })

function mapDispatchToProps(dispatch) {
  return bindActionCreators({
    push: routeActions.push,
    //all your other action creators here
  }, dispatch)
}

export const CmptContainer = connect({}, mapDispatchToProps)(Cmpt)

Then you can use push via props this.props.push('some/cool/route')

Ryan Irilli
  • 1,067
  • 8
  • 5
1

More Concisely

For these examples, I'd curry the mapDispatchToProps function like so:

bindActionDispatchers.js

import { bindActionCreators } from 'redux'

/** curries mapDispatchToProps with bindActionCreators to simplify React action dispatchers. */
export default function bindActionDispatchers(actionCreators) {
  if(typeof actionCreators === 'function')
    return (dispatch, ownProps) => bindActionCreators(actionCreators(ownProps), dispatch)
  return dispatch => bindActionCreators(actionCreators, dispatch)
}

Foo.js

import React from 'react'
import bindActionDispatchers from './bindActionDispatchers'
import { routeActions } from 'react-router-redux'
import * as appActions from './actions'

const Foo = ({ routeActions ...appActions }) => (
  <div>
    <button onClick={routeActions.push('/route')}>Route</button>
    <button onClick={appActions.hello('world')}>Hello</button>
  </div>
)

export default connect(null, bindActionDispatchers({ routeActions, ...appActions }))(Foo)

I packaged this into a lightweight npm package bind-action-dispatchers complete with unit tests and sanity checks. To use this version install with:

npm i -S bind-action-dispatchers

and import the function with:

import bindActionDispatchers from 'bind-action-dispatchers'

Community
  • 1
  • 1
cchamberlain
  • 14,693
  • 6
  • 52
  • 67