Connect: Dispatching Actions with mapDispatchToProps
As the second argument passed in to connect
, mapDispatchToProps
is used for dispatching actions to the store.
dispatch
is a function of the Redux store. You call store.dispatch
to dispatch an action.
This is the only way to trigger a state change.
With Redux Toolkit, your components never access the store directly - connect
does it for you.
Redux Toolkit gives you two ways to let components dispatch actions:
- By default, a connected component receives
props.dispatch
and can dispatch actions itself. connect
can accept an argument calledmapDispatchToProps
, which lets you create functions that dispatch when called, and pass those functions as props to your component.
The mapDispatchToProps
functions are normally referred to as mapDispatch
for short, but the actual variable name used can be whatever you want.
Approaches for Dispatching
Default: dispatch
as a Prop
If you don't specify the second argument to connect()
, your component will receive dispatch
by default. For example:
connect()(MyComponent)
// which is equivalent with
connect(null, null)(MyComponent)
// or
connect(mapStateToProps /** no second argument */)(MyComponent)
Once you have connected your component in this way, your component receives props.dispatch
. You may use it to dispatch actions to the store.
function Counter({ count, dispatch }) {
return (
<div>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>-</button>
<span>{count}</span>
<button onClick={() => dispatch({ type: 'INCREMENT' })}>+</button>
<button onClick={() => dispatch({ type: 'RESET' })}>reset</button>
</div>
)
}
Providing A mapDispatchToProps
Parameter
Providing a mapDispatchToProps
allows you to specify which actions your component might need to dispatch. It lets you provide action dispatching functions as props. Therefore, instead of calling props.dispatch(() => increment())
, you may call props.increment()
directly. There are a few reasons why you might want to do that.
More Declarative
First, encapsulating the dispatch logic into function makes the implementation more declarative. Dispatching an action and letting the Redux store handle the data flow is how to implement the behavior, rather than what it does.
A good example would be dispatching an action when a button is clicked. Connecting the button directly probably doesn't make sense conceptually, and neither does having the button reference dispatch
.
// button needs to be aware of "dispatch"
<button onClick={() => dispatch({ type: "SOMETHING" })} />
// button unaware of "dispatch",
<button onClick={doSomething} />
Once you've wrapped all our action creators with functions that dispatch the actions, the component is free of the need of dispatch
.
Therefore, if you define your own mapDispatchToProps
, the connected component will no longer receive dispatch
.
Pass Down Action Dispatching Logic to ( Unconnected ) Child Components
In addition, you also gain the ability to pass down the action dispatching functions to child ( likely unconnected ) components. This allows more components to dispatch actions, while keeping them "unaware" of Redux.
// pass down toggleTodo to child component
// making Todo able to dispatch the toggleTodo action
const TodoList = ({ todos, toggleTodo }) => (
<div>
{todos.map((todo) => (
<Todo todo={todo} onClick={toggleTodo} />
))}
</div>
)
This is what Redux Toolkit’s connect
does — it encapsulates the logic of talking to the Redux store and lets you not worry about it. And this is what you should totally make full use of in your implementation.
Two Forms of mapDispatchToProps
The mapDispatchToProps
parameter can be of two forms. While the function form allows more customization, the object form is easy to use.
- Function form: Allows more customization, gains access to
dispatch
and optionallyownProps
- Object shorthand form: More declarative and easier to use
⭐ Note: We recommend using the object form of
mapDispatchToProps
unless you specifically need to customize dispatching behavior in some way.
Defining mapDispatchToProps
As A Function
Defining mapDispatchToProps
as a function gives you the most flexibility in customizing the functions your component receives, and how they dispatch actions.
You gain access to dispatch
and ownProps
.
You may use this chance to write customized functions to be called by your connected components.
Arguments
dispatch
ownProps
(optional)
dispatch
The mapDispatchToProps
function will be called with dispatch
as the first argument.
You will normally make use of this by returning new functions that call dispatch()
inside themselves, and either pass in a plain action object directly or pass in the result of an action creator.
const mapDispatchToProps = (dispatch) => {
return {
// dispatching plain actions
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
reset: () => dispatch({ type: 'RESET' }),
}
}
You will also likely want to forward arguments to your action creators:
const mapDispatchToProps = (dispatch) => {
return {
// explicitly forwarding arguments
onClick: (event) => dispatch(trackClick(event)),
// implicitly forwarding arguments
onReceiveImpressions: (...impressions) =>
dispatch(trackImpressions(impressions)),
}
}
ownProps
( optional )
If your mapDispatchToProps
function is declared as taking two parameters, it will be called with dispatch
as the first parameter and the props
passed to the connected component as the second parameter, and will be re-invoked whenever the connected component receives new props.
This means, instead of re-binding new props
to action dispatchers upon component re-rendering, you may do so when your component's props
change.
Binds on component mount
render() {
return <button onClick={() => this.props.toggleTodo(this.props.todoId)} />
}
const mapDispatchToProps = dispatch => {
return {
toggleTodo: todoId => dispatch(toggleTodo(todoId))
}
}
Binds on props
change
render() {
return <button onClick={() => this.props.toggleTodo()} />
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
toggleTodo: () => dispatch(toggleTodo(ownProps.todoId))
}
}
Return
Your mapDispatchToProps
function should return a plain object:
- Each field in the object will become a separate prop for your own component, and the value should normally be a function that dispatches an action when called.
- If you use action creators ( as oppose to plain object actions ) inside
dispatch
, it is a convention to simply name the field key the same name as the action creator:
const increment = () => ({ type: 'INCREMENT' })
const decrement = () => ({ type: 'DECREMENT' })
const reset = () => ({ type: 'RESET' })
const mapDispatchToProps = (dispatch) => {
return {
// dispatching actions returned by action creators
increment: () => dispatch(increment()),
decrement: () => dispatch(decrement()),
reset: () => dispatch(reset()),
}
}
The return of the mapDispatchToProps
function will be merged to your connected component as props. You may call them directly to dispatch its action.
function Counter({ count, increment, decrement, reset }) {
return (
<div>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
<button onClick={reset}>reset</button>
</div>
)
}
(Full code of the Counter example is in this CodeSandbox)
Defining the mapDispatchToProps
Function with bindActionCreators
Wrapping these functions by hand is tedious, so Redux provides a function to simplify that.
bindActionCreators
turns an object whose values are action creators, into an object with the same keys, but with every action creator wrapped into adispatch
call so they may be invoked directly. See Redux Docs onbindActionCreators
bindActionCreators
accepts two parameters:
- A
function
(an action creator) or anobject
(each field an action creator) dispatch
The wrapper functions generated by bindActionCreators
will automatically forward all of their arguments, so you don't need to do that by hand.
import { bindActionCreators } from 'redux'
const increment = () => ({ type: 'INCREMENT' })
const decrement = () => ({ type: 'DECREMENT' })
const reset = () => ({ type: 'RESET' })
// binding an action creator
// returns (...args) => dispatch(increment(...args))
const boundIncrement = bindActionCreators(increment, dispatch)
// binding an object full of action creators
const boundActionCreators = bindActionCreators(
{ increment, decrement, reset },
dispatch
)
// returns
// {
// increment: (...args) => dispatch(increment(...args)),
// decrement: (...args) => dispatch(decrement(...args)),
// reset: (...args) => dispatch(reset(...args)),
// }
To use bindActionCreators
in our mapDispatchToProps
function:
import { bindActionCreators } from 'redux'
// ...
function mapDispatchToProps(dispatch) {
return bindActionCreators({ increment, decrement, reset }, dispatch)
}
// component receives props.increment, props.decrement, props.reset
connect(null, mapDispatchToProps)(Counter)
Manually Injecting dispatch
If the mapDispatchToProps
argument is supplied, the component will no longer receive the default dispatch
. You may bring it back by adding it manually to the return of your mapDispatchToProps
, although most of the time you shouldn’t need to do this:
import { bindActionCreators } from 'redux'
// ...
function mapDispatchToProps(dispatch) {
return {
dispatch,
...bindActionCreators({ increment, decrement, reset }, dispatch),
}
}
Defining mapDispatchToProps
As An Object
You’ve seen that the setup for dispatching Redux actions in a React component follows a very similar process: define an action creator, wrap it in another function that looks like (…args) => dispatch(actionCreator(…args))
, and pass that wrapper function as a prop to your component.
Because this is so common, connect
supports an “object shorthand” form for the mapDispatchToProps
argument: if you pass an object full of action creators instead of a function, connect
will automatically call bindActionCreators
for you internally.
We recommend always using the “object shorthand” form of mapDispatchToProps
, unless you have a specific reason to customize the dispatching behavior.
Note that:
- Each field of the
mapDispatchToProps
object is assumed to be an action creator - Your component will no longer receive
dispatch
as a prop
// Redux Toolkit does this for you automatically:
;(dispatch) => bindActionCreators(mapDispatchToProps, dispatch)
Therefore, our mapDispatchToProps
can simply be:
const mapDispatchToProps = {
increment,
decrement,
reset,
}
Since the actual name of the variable is up to you, you might want to give it a name like actionCreators
, or even define the object inline in the call to connect
:
import { increment, decrement, reset } from './counterActions'
const actionCreators = {
increment,
decrement,
reset,
}
export default connect(mapState, actionCreators)(Counter)
// or
export default connect(mapState, { increment, decrement, reset })(Counter)
Common Problems
Why is my component not receiving dispatch
?
Also known as
TypeError: this.props.dispatch is not a function
This is a common error that happens when you try to call this.props.dispatch
, but dispatch
is not injected to your component.
dispatch
is injected to your component only when:
1. You do not provide mapDispatchToProps
The default mapDispatchToProps
is simply dispatch => ({ dispatch })
. If you do not provide mapDispatchToProps
, dispatch
will be provided as mentioned above.
In another words, if you do:
// component receives `dispatch`
connect(mapStateToProps /** no second argument*/)(Component)
2. Your customized mapDispatchToProps
function return specifically contains dispatch
You may bring back dispatch
by providing your customized mapDispatchToProps
function:
const mapDispatchToProps = (dispatch) => {
return {
increment: () => dispatch(increment()),
decrement: () => dispatch(decrement()),
reset: () => dispatch(reset()),
dispatch,
}
}
Or alternatively, with bindActionCreators
:
import { bindActionCreators } from 'redux'
function mapDispatchToProps(dispatch) {
return {
dispatch,
...bindActionCreators({ increment, decrement, reset }, dispatch),
}
}
See this error in action in Redux’s GitHub issue #255.
There are discussions regarding whether to provide dispatch
to your components when you specify mapDispatchToProps
( Dan Abramov’s response to #255 ). You may read them for further understanding of the current implementation intention.
Can I mapDispatchToProps
without mapStateToProps
in Redux?
Yes. You can skip the first parameter by passing undefined
or null
. Your component will not subscribe to the store, and will still receive the dispatch props defined by mapDispatchToProps
.
connect(null, mapDispatchToProps)(MyComponent)
Can I call store.dispatch
?
It's an anti-pattern to interact with the store directly in a React component, whether it's an explicit import of the store or accessing it via context (see the Redux FAQ entry on store setup for more details). Let Redux Toolkit’s connect
handle the access to the store, and use the dispatch
it passes to the props to dispatch actions.
Links and References
Tutorials
Related Docs
Q&A