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