Curry away in React
So you’re going to build the next-gen eCommerce company selling dog supplies and you’ve decided to use React to build it with coz, you know, its all the rage now and choosing a front-end framework is the “first and most important responsible step” towards building any successful online business.
I’d like to share a neat trick I learned while building my own eCommerce website, obviously next-gen coz React!
Filtering products based on various aspects is a staple in any shopping site and so let’s add some filters to the product results page.
This is where our customers filter and find the right products for their dogs
As any ‘good’ React developer would, let’s break up this UI into components which is pretty straight forward in our case.
This is what React developers see when they visit any webpage
Next, let’s identify the state in our application. The only pieces of data that maybe considered as state here are the filter selections. The other data, the product list would likely be passed down as props and are not mutated.
filterSelections
is an object which contains the selected value for each of the filters in our page.
filterSelections = {
price: ...,
ages: ...,
brands: ...,
}
Since the Products
component is the common parent that contains the components that need filterSelections
(the Filters
and ProductResults
components), it would be the appropriate home for this state to live in.
The Filters
component, after receiving the filterSelections
as a prop, will pass down relevant filter values to each of its children.
class Filters extends React.Component {
render() {
return (
<>
<PriceFilter price={this.props.filterSelections.price} />
<AgeFilter ages={this.props.filterSelections.ages} />
<BrandFilter brands={this.props.filterSelections.brands} />
</>
);
};
}
filterSelections
will also be passed down into the ProductResults
component so that the filters can be applied and only the relevant products are shown.
These filters can’t be just static controls, we need to them to update filterSelections
as the customer adjusts the filters.
We’re passing down filterSelections
as props to the filters and we know props are immutable in React. So where do we mutate filterSelections
then? The answer to that would be the Products
component as its the owner of filterSelections
having it as its state.
Let’s go ahead and add a bunch of change handler functions so that any changes to the filter selections are propagated up to the Products
component where the actual mutation happens.
Here’s the Products
component.
class Products extends React.Component {
constructor(props) {
super(props);
this.state = {
filterSelections: {
price: someInitialValue,
ages: someInitialValue,
brands: someInitialValue,
}
}
}
updateFilters = (newSelections) => {
this.setState({
filterSelections: newSelections
})
};
render() {
return(
<>
<Filters
filterSelections={this.state.filterSelections}
selectionsChanged={this.updateFilters}
/>
<Products filterSelections={this.state.filterSelections} />
</> // React 16 Fragment Syntax - whoohoo!
);
}
}
And the Filters
component.
class Filters extends React.Component {
updatePriceFilter = (newValue) => {
this.props.selectionsChanged({
...this.props.filterSelections,
price: newValue
})
};
updateAgeFilter = (newValue) => {
this.props.selectionsChanged({
...this.props.filterSelections,
ages: newValue
})
};
updateBrandFilter = (newValue) => {
this.props.selectionsChanged({
...this.props.filterSelections,
brands: newValue
})
};
render() {
return (
<>
<PriceFilter
price={this.props.filterSelections.price}
priceChanged={this.updatePriceFilter}
/>
<AgeFilter
ages={this.props.filterSelections.ages}
agesChanged={this.updateAgeFilter}
/>
<BrandFilter
brands={this.props.filterSelections.brands}
brandsChanged={this.updateBrandFilter}
/>
</>
);
};
}
This is pretty straightforward and would work. But we’ve got a bunch of functions in the Filters
component, all of which seem to be doing very similar things.
And if we add a new kind of filter in future, we’ll need to write yet another similar looking function in this component.
Currying to the rescue
This is where the trick comes in and it’s a popular & fancy sounding functional programming concept called currying.
Before talking about currying, I’d like to show you how it can help clean up our component. Take a look for yourself —
class Filters extends React.Component {
updateSelections = (selectionType) => {
return (newValue) => {
this.props.selectionsChanged({
...this.props.selections,
[selectionType]: newValue, // new ES6 Syntax!! :)
});
}
};
render() {
return (
<>
<PriceFilter
price={this.props.selections.price}
priceChanged={this.updateSelections('price')}
/>
<AgeFilter
ages={this.props.selections.ages}
agesChanged={this.updateSelections('ages')}
/>
<BrandFilter
brands={this.props.selections.brands}
brandsChanged={this.updateSelections('brands')}
/>
</>
);
};
}
Neat right? Now the updatedSelections
function is said to be curried. For the functional programming aficionados out there, this is a pretty simple and straightforward concept. But for folks like me who’s always lived in the imperative & object oriented side of things, this was confusing to understand. So let’s unpack it.
You might have wondered when I said that the updatedSelections()
function has been curried, what the “uncurried” version of this function would look like. Here’s how —
updateSelections = (selectionType, value) => {
this.props.updateFilters({
...this.props.filterSelections,
[selectionType]: newValue,
});
}
If we were to use this instead of our curried version, we’d need to make each of the Filter component children call updateSelections
themselves and thus having to make them aware of the attribute name in filterSelections
that they need to update, which is too much coupling.
The alternative is to use a dedicated function for each of the Filter components which leads to a mess like we saw already.
Our solution is to curry this function.
What’s currying?
Currying is transforming a function f
into a function f'
which takes part of the arguments that f
originally needed and would return another function which could take in rest of the arguments, returning the result of f
or could be curried itself.
Concretely, take this simple add
function,
add = (x, y) => x + y;
when curried, it becomes —
curriedAdd = (x) => { return (y) => { return x + y; }}
So instead of calling add(1, 2)
we could call curriedAdd(1)
which returns a new function, which we could subsequently call with the other argument of add
, like curriedAdd(1)(2)
and get the final result 3
.
Technically calling, curriedAdd(x)
is called “partial application”, as in, we’re applying only part of the arguments that the original function add(X, y)
needed.
Currying a regular function let’s us perform partial application on it.
This is a naive toy example which does not have much utility.
Now if we go back to our original example —
updateSelections = (selectionType) => {
return (newValue) => {
this.props.selectionsChanged({
...this.props.filterSelections,
[selectionType]: newValue,
});
}
};
we can see how partial applications of the updateSelections
function —
updateSelections('ages');updateSelections('brands');updateSelections('price');
helped us clean up our component and reduce coupling. That’s currying
Like all problems in software, this is not the only solution to this problem. Here’s an alternate solution —
class Filters extends React.Component {
updateSelections = (selectionType, newValue) => {
this.props.selectionsChanged({
...this.props.filterSelections,
[selectionType]: newValue,
});
};
render() {
return (
<>
<PriceFilter
price={this.props.selections.price}
priceChanged={(value) => this.updateSelections('price', value)}
/>
<AgeFilter
ages={this.props.selections.ages}
agesChanged={(value) => this.updateSelections('ages', value)}
/>
<BrandFilter
brands={this.props.selections.brands}
brandsChanged={(value) => this.updateSelections('brands', value)}
/>
</>
);
};
}
I hope you found this technique useful. If you’ve used currying in your JavaScript / React project, do share snippets in the comments below and also your other comments and feedback are welcome ❤.