Currying Action Creators in React Redux
May 7, 2018
I ran into a situation where I wanted to curry an action creator. Without giving it much thought, I gave the following a shot
const addNumber = (id) => () => ({ type, payload: { id } });
which I passed to my component through mapDispatchToProps
through the connect
higher order component. This didn't work. After a few minutes I realized my curried function was being wrapped by dispatch
and I wouldn't be able to partially apply it like I wanted to. You can read more about how mapDispatchToProps
works here: https://github.com/reactjs/react-redux/blob/master/docs/api.md
The trick, I realized, was to create the curried function after the action creator had been wrapped by dispatch.
My contrived example
I wrote the following application based on a create-react-app
starter project. To replicate the same locally just follow these quick steps:
-
create-react-app curry && cd curry
-
yarn add redux react-redux
-
yarn start
and then paste the following code over path/to/curry/src/App.js
:
import React, { Component } from 'react'; import { connect, Provider } from 'react-redux'; import { createStore } from 'redux'; const initialState = { item0: 0, item1: 0, item2: 0, item3: 0, item4: 0, item5: 0, } const myNumber = (state, {type, payload}) => { switch(type) { case 'ADD': return { ...state, [payload.itemId]: state[payload.itemId] + 1, } default: return state; }; }; const store = createStore(myNumber, initialState); class App extends Component { render() { return ( <Provider store={store}> <ListContainer /> </Provider> ); } } export default App; class ListContainer extends Component { constructor(props) { super(props); const { addNumber } = this.props; // In the constructor so a new object instance of this function is not created every render this.curriedAddNumber = (itemId) => () => addNumber(itemId); } render() { const { items } = this.props; const itemKeys = Object.keys(items); return ( <div> { itemKeys.map((itemId, index) => ( <ListItem key={itemId} number={items[itemId]} addNumber={this.curriedAddNumber(itemId)} /> )) } </div> ); } } const mapStateToProps = (state) => ({ items: state }); const addNumber = (itemId) => ({ type: 'ADD', payload: { itemId } }); ListContainer = connect( mapStateToProps, { addNumber }, )(ListContainer) const style = { container: { display: 'flex', border: '1px solid black', padding: 5, margin: 5, }, num: { flex: 4, textAlign: 'center', }, button: { flex: 1 }, }; const ListItem = ({ addNumber, number, id }) => ( <div style={style.container}> <div style={style.num}>{number}</div> <button style={style.button} onClick={addNumber} > Add Number </button> </div> );
Explanation
In this example, curriedAddNumber
is created after addNumber
is wrapped by dispatch
by mapDispatchToProps
. This allows me to partially apply curriedAddNumber
as I originally intended.
This might seem more complicated than necessary, but take a moment to think of the implications. Now my child component has a function to add a number to the redux state, but doesn't even know which ListItem
it is! The example I ran into and puzzled through this was quite a bit more complicated, but if you googled the question and found the title, then I imagine you should be able to take it from here :). Please comment if you have any questions.
Cheers