164

I am building an small application with redux, react-redux, & react. For some reason when using mapDispatchToProps function in tandem with connect (react-redux binding) I receive a TypeError indicating that dispatch is not a function when I try to execute the resulting prop. When I call dispatch as a prop however (see the setAddr function in the provided code) it works.

I'm curious as to why this is, in the example TODO app in the redux docs the mapDispatchToProps method is setup the same way. When I console.log(dispatch) inside the function it says dispatch is type object. I could continue to use dispatch this way but I would feel better knowing why this is happening before I continue any further with redux. I am using webpack with babel-loaders to compile.

My Code:

import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
import { setAddresses } from '../actions.js';
import GeoCode from './geoCode.js';
import FlatButton from 'material-ui/lib/flat-button';

const Start = React.createClass({
    propTypes: {
        onSubmit: PropTypes.func.isRequired
    },

    setAddr: function(){
        this.props.dispatch(
            setAddresses({
                pickup: this.refs.pickup.state.address,
                dropoff: this.refs.dropoff.state.address
            })
        )   

    },

    render: function() {
        return (
            <div>
                <div className="row">
                    <div className="col-xs-6">
                        <GeoCode ref='pickup' />
                    </div>
                    <div className="col-xs-6">
                        <GeoCode ref='dropoff' />
                    </div>
                </div>
                <div className="row">
                    <div className="col-xs-6">
                        <FlatButton 
                            label='Does Not Work'
                            onClick={this.props.onSubmit({
                                pickup: this.refs.pickup.state.address,
                                dropoff: this.refs.dropoff.state.address
                            })} 
                            />
                    </div>
                    <div className="col-xs-6">
                        <FlatButton 
                            label='Works'
                            onClick={this.setAddr} 
                            />
                    </div>
                </div>
            </div>
        );
    }
})

const mapDispatchToProps = (dispatch) => {
    return {
        onSubmit: (data) => {
            dispatch(setAddresses(data))
        }
    }
}

const StartContainer = connect(mapDispatchToProps)(Start)

export default StartContainer
starball
  • 20,030
  • 7
  • 43
  • 238
condad
  • 1,765
  • 2
  • 12
  • 9
  • I was having this issue and [this](https://stackoverflow.com/a/47565406/1399491) helped me solve it – Alex W Jul 08 '20 at 18:42

13 Answers13

347

If you want to use mapDispatchToProps without a mapStateToProps just use null for the first argument.

export default connect(null, mapDispatchToProps)(Start)

inostia
  • 7,777
  • 3
  • 30
  • 33
  • 6
    This is what I have been looking for. Now I know if I just want to dispatch some actions. – Emperor Krauser Mar 09 '17 at 18:55
  • This is exactly something I was looking for. Thanks for the perfect solution. What if we don't have to use mapDispatchToProps in any of our component ? Is it the same way we have to handle that scenario ? – Arun Ramachandran Mar 26 '18 at 13:33
  • 1
    If you don't need `mapDispatchToProps`, just don't add that argument to `connect`: `connect(mapStateToProps)(MyComponent)` – inostia Mar 26 '18 at 17:08
116

You are just missing the first argument to connect, which is the mapStateToProps method. Excerpt from the Redux todo app:

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

const VisibleTodoList = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)
Brandon
  • 38,310
  • 8
  • 82
  • 87
24

Use

const StartContainer = connect(null, mapDispatchToProps)(Start) 

instead of

const StartContainer = connect(mapDispatchToProps)(Start)
ρяσѕρєя K
  • 132,198
  • 53
  • 198
  • 213
Ann
  • 249
  • 2
  • 2
7

I solved it by interchanging the arguments, I was using

export default connect(mapDispatchToProps, mapStateToProps)(Checkbox)

which is wrong. The mapStateToProps has to be the first argument:

export default connect(mapStateToProps, mapDispatchToProps)(Checkbox)

It sounds obvious now, but might help someone.

Derek Brown
  • 4,232
  • 4
  • 27
  • 44
Amit Mittal
  • 85
  • 2
  • 6
4

I needed an example using React.Component so I am posting it:

import React from 'react';
import * as Redux from 'react-redux';

class NavigationHeader extends React.Component {

}

const mapStateToProps = function (store) {
    console.log(`mapStateToProps ${store}`);
    return {
        navigation: store.navigation
    };
};

export default Redux.connect(mapStateToProps)(NavigationHeader);
Amio.io
  • 20,677
  • 15
  • 82
  • 117
4

Issue

Here are a couple of things to notice in order to understand the connected component's behavior in your code:

The Arity of connect Matters: connect(mapStateToProps, mapDispatchToProps)

React-Redux calls connect with the first argument mapStateToProps, and second argument mapDispatchToProps.

Therefore, although you've passed in your mapDispatchToProps, React-Redux in fact treats that as mapState because it is the first argument. You still get the injected onSubmit function in your component because the return of mapState is merged into your component's props. But that is not how mapDispatch is supposed to be injected.

You may use mapDispatch without defining mapState. Pass in null in place of mapState and your component will not subject to store changes.

Connected Component Receives dispatch by Default, When No mapDispatch Is Provided

Also, your component receives dispatch because it received null for its second position for mapDispatch. If you properly pass in mapDispatch, your component will not receive dispatch.

Common Practice

The above answers why the component behaved that way. Although, it is common practice that you simply pass in your action creator using mapStateToProps's object shorthand. And call that within your component's onSubmit That is:

import { setAddresses } from '../actions.js'

const Start = (props) => {
  // ... omitted
  return <div>
    {/** omitted */}
    <FlatButton 
       label='Does Not Work'
       onClick={this.props.setAddresses({
         pickup: this.refs.pickup.state.address,
         dropoff: this.refs.dropoff.state.address
       })} 
    />
  </div>
};

const mapStateToProps = { setAddresses };
export default connect(null, mapDispatchToProps)(Start)
wgao19
  • 523
  • 5
  • 7
2

A pitfall some might step into that is covered by this question but isn't addressed in the answers as it is slightly different in the code structure but returns the exact same error.

This error occurs when using bindActionCreators and not passing the dispatch function

Error Code

import someComponent from './someComponent'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'
import { someAction } from '../../../actions/someAction'

const mapStatToProps = (state) => {
    const { someState } = state.someState

    return {
        someState
    }
};

const mapDispatchToProps = (dispatch) => {
    return bindActionCreators({
        someAction
    });
};

export default connect(mapStatToProps, mapDispatchToProps)(someComponent)

Fixed Code

import someComponent from './someComponent'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'
import { someAction } from '../../../actions/someAction'

const mapStatToProps = (state) => {
    const { someState } = state.someState

    return {
        someState
    }
};

const mapDispatchToProps = (dispatch) => {
    return bindActionCreators({
        someAction
    }, dispatch);
};

export default connect(mapStatToProps, mapDispatchToProps)(someComponent)

The function dispatch was missing in the Error code

imbatman
  • 508
  • 5
  • 16
2

React-redux 'connect' function accepts two arguments first is mapStateToProps and second is mapDispatchToProps check below ex.

export default connect(mapStateToProps, mapDispatchToProps)(Index); `

If we don't want retrieve state from redux then we set null instead of mapStateToProps.

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

1

You're missing in the last statement. As we don't have mapStateToProps, so the statement will be like below

const StartContainer = connect(null, mapDispatchToProps)(Start) 
Elikill58
  • 4,050
  • 24
  • 23
  • 45
0

When you do not provide mapDispatchToProps as a second argument, like this:

export default connect(mapStateToProps)(Checkbox)

then you are automatically getting the dispatch to component's props, so you can just:

class SomeComp extends React.Component {
  constructor(props, context) {
    super(props, context);
  }
  componentDidMount() {
    this.props.dispatch(ACTION GOES HERE);
  }
....

without any mapDispatchToProps

Sergio Nikolaev
  • 679
  • 8
  • 16
0

i am using like this.. its easy to understand first argument is mapStateToProps and second argument is mapDispatchToProps in the end connect with function/class.

const mapStateToProps = (state) => {
  return {
    todos: getVisibleTodos(state.todos, state.visibilityFilter)
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    onTodoClick: (id) => {
      dispatch(toggleTodo(id))
    }
  }
}

export default connect(mapStateToProps,mapDispatchToProps)(TodoList);
Abdul Moiz
  • 317
  • 3
  • 8
0

Sometime this error also occur when you change the order of Component Function while passing to connect.

Incorrect Order:

export default connect(mapDispatchToProps, mapStateToProps)(TodoList);

Correct Order:

export default connect(mapStateToProps,mapDispatchToProps)(TodoList);
Codiee
  • 3,047
  • 2
  • 17
  • 18
0

I got this issue when i wrote :
export default connect (mapDispatchToProps,mapStateToProps)(SearchInsectsComponent); instead of export default connect (mapStateToProps,mapDispatchToProps)(SearchInsectsComponent);

harika
  • 21
  • 3