Connect: Dispatching Actions withmapDispatchToProps
As the second argument passed in toconnect,mapDispatchToProps is used for dispatching actions to the store.
dispatch is a function of the Redux store. You callstore.dispatch to dispatch an action.This is the only way to trigger a state change.
With React Redux, your components never access the store directly -connect does it for you.React Redux gives you two ways to let components dispatch actions:
- By default, a connected component receives
props.dispatchand can dispatch actions itself. connectcan accept an argument calledmapDispatchToProps, which lets you create functions that dispatch when called, and pass those functions as props to your component.
ThemapDispatchToProps functions are normally referred to asmapDispatch 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 toconnect(), your component will receivedispatch 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 receivesprops.dispatch. You may use it to dispatch actions to the store.
functionCounter({ 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 AmapDispatchToProps Parameter
Providing amapDispatchToProps allows you to specify which actions your component might need to dispatch. It lets you provide action dispatching functions as props. Therefore, instead of callingprops.dispatch(() => increment()), you may callprops.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 ishow to implement the behavior, rather thanwhat 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 referencedispatch.
// 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 ofdispatch.Therefore,if you define your ownmapDispatchToProps, the connected component will no longer receivedispatch.
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
constTodoList=({ todos, toggleTodo})=>(
<div>
{todos.map((todo)=>(
<Todotodo={todo}onClick={toggleTodo}/>
))}
</div>
)
This is what React Redux’sconnect 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 ofmapDispatchToProps
ThemapDispatchToProps 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
dispatchand optionallyownProps - Object shorthand form: More declarative and easier to use
⭐Note: We recommend using the object form of
mapDispatchToPropsunless you specifically need to customize dispatching behavior in some way.
DefiningmapDispatchToProps As A Function
DefiningmapDispatchToProps as a function gives you the most flexibility in customizing the functions your component receives, and how they dispatch actions.You gain access todispatch andownProps.You may use this chance to write customized functions to be called by your connected components.
Arguments
dispatchownProps(optional)
dispatch
ThemapDispatchToProps function will be called withdispatch as the first argument.You will normally make use of this by returning new functions that calldispatch() inside themselves, and either pass in a plain action object directly or pass in the result of an action creator.
constmapDispatchToProps=(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:
constmapDispatchToProps=(dispatch)=>{
return{
// explicitly forwarding arguments
onClick:(event)=>dispatch(trackClick(event)),
// implicitly forwarding arguments
onReceiveImpressions:(...impressions)=>
dispatch(trackImpressions(impressions)),
}
}
ownProps ( optional )
If yourmapDispatchToProps function is declared as taking two parameters, it will be called withdispatch as the first parameter and theprops 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 newprops to action dispatchers upon component re-rendering, you may do so when your component'sprops change.
Binds on component mount
render(){
return<button onClick={()=>this.props.toggleTodo(this.props.todoId)}/>
}
constmapDispatchToProps=dispatch=>{
return{
toggleTodo:todoId=>dispatch(toggleTodo(todoId))
}
}
Binds onprops change
render(){
return<button onClick={()=>this.props.toggleTodo()}/>
}
constmapDispatchToProps=(dispatch, ownProps)=>{
return{
toggleTodo:()=>dispatch(toggleTodo(ownProps.todoId))
}
}
Return
YourmapDispatchToProps 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:
constincrement=()=>({type:'INCREMENT'})
constdecrement=()=>({type:'DECREMENT'})
constreset=()=>({type:'RESET'})
constmapDispatchToProps=(dispatch)=>{
return{
// dispatching actions returned by action creators
increment:()=>dispatch(increment()),
decrement:()=>dispatch(decrement()),
reset:()=>dispatch(reset()),
}
}
The return of themapDispatchToProps function will be merged to your connected component as props. You may call them directly to dispatch its action.
functionCounter({ 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 isin this CodeSandbox)
Defining themapDispatchToProps Function withbindActionCreators
Wrapping these functions by hand is tedious, so Redux provides a function to simplify that.
bindActionCreatorsturns an object whose values areaction creators, into an object with the same keys, but with every action creator wrapped into adispatchcall so they may be invoked directly. SeeRedux Docs onbindActionCreators
bindActionCreators accepts two parameters:
- A
function(an action creator) or anobject(each field an action creator) dispatch
The wrapper functions generated bybindActionCreators will automatically forward all of their arguments, so you don't need to do that by hand.
import{ bindActionCreators}from'redux'
constincrement=()=>({type:'INCREMENT'})
constdecrement=()=>({type:'DECREMENT'})
constreset=()=>({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 usebindActionCreators in ourmapDispatchToProps function:
import{ bindActionCreators}from'redux'
// ...
functionmapDispatchToProps(dispatch){
returnbindActionCreators({ increment, decrement, reset}, dispatch)
}
// component receives props.increment, props.decrement, props.reset
connect(null, mapDispatchToProps)(Counter)
Manually Injectingdispatch
If themapDispatchToProps argument is supplied, the component will no longer receive the defaultdispatch. You may bring it back by adding it manually to the return of yourmapDispatchToProps, although most of the time you shouldn’t need to do this:
import{ bindActionCreators}from'redux'
// ...
functionmapDispatchToProps(dispatch){
return{
dispatch,
...bindActionCreators({ increment, decrement, reset}, dispatch),
}
}
DefiningmapDispatchToProps 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 themapDispatchToProps argument: if you pass an object full of action creators instead of a function,connect will automatically callbindActionCreators for you internally.
We recommend always using the “object shorthand” form ofmapDispatchToProps, unless you have a specific reason to customize the dispatching behavior.
Note that:
- Each field of the
mapDispatchToPropsobject is assumed to be an action creator - Your component will no longer receive
dispatchas a prop
// React Redux does this for you automatically:
;(dispatch)=>bindActionCreators(mapDispatchToProps, dispatch)
Therefore, ourmapDispatchToProps 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 likeactionCreators, or even define the object inline in the call toconnect:
import{ increment, decrement, reset}from'./counterActions'
const actionCreators={
increment,
decrement,
reset,
}
exportdefaultconnect(mapState, actionCreators)(Counter)
// or
exportdefaultconnect(mapState,{ increment, decrement, reset})(Counter)
Common Problems
Why is my component not receivingdispatch?
Also known as
TypeError:this.props.dispatch is not afunction
This is a common error that happens when you try to callthis.props.dispatch , butdispatch is not injected to your component.
dispatch is injected to your componentonly when:
1. You do not providemapDispatchToProps
The defaultmapDispatchToProps is simplydispatch => ({ dispatch }). If you do not providemapDispatchToProps,dispatch will be provided as mentioned above.
In another words, if you do:
// component receives `dispatch`
connect(mapStateToProps/** no second argument*/)(Component)
2. Your customizedmapDispatchToProps function return specifically containsdispatch
You may bring backdispatch by providing your customizedmapDispatchToProps function:
constmapDispatchToProps=(dispatch)=>{
return{
increment:()=>dispatch(increment()),
decrement:()=>dispatch(decrement()),
reset:()=>dispatch(reset()),
dispatch,
}
}
Or alternatively, withbindActionCreators:
import{ bindActionCreators}from'redux'
functionmapDispatchToProps(dispatch){
return{
dispatch,
...bindActionCreators({ increment, decrement, reset}, dispatch),
}
}
Seethis error in action in Redux’s GitHub issue #255.
There are discussions regarding whether to providedispatch to your components when you specifymapDispatchToProps (Dan Abramov’s response to #255 ). You may read them for further understanding of the current implementation intention.
Can ImapDispatchToProps withoutmapStateToProps in Redux?
Yes. You can skip the first parameter by passingundefined ornull. Your component will not subscribe to the store, and will still receive the dispatch props defined bymapDispatchToProps.
connect(null, mapDispatchToProps)(MyComponent)
Can I callstore.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 theRedux FAQ entry on store setup for more details). Let React Redux’sconnect handle the access to the store, and use thedispatch it passes to the props to dispatch actions.
Links and References
Tutorials
Related Docs
Q&A