I have been reading a bunch of React code and I see stuff like this that I don’t understand:
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
7 s
That is a curried function
First, examine this function with two parameters …
const add = (x, y) => x + y
add(2, 3) //=> 5
Here it is again in curried form …
const add = x => y => x + y
Here is the same1 code without arrow functions …
const add = function (x) {
return function (y) {
return x + y
}
}
Focus on return
It might help to visualize it another way. We know that arrow functions work like this – let’s pay particular attention to the return value.
const f = someParam => returnValue
So our add
function returns a function – we can use parentheses for added clarity. The bolded text is the return value of our function add
const add = x => (y => x + y)
In other words add
of some number returns a function
add(2) // returns (y => 2 + y)
Calling curried functions
So in order to use our curried function, we have to call it a bit differently …
add(2)(3) // returns 5
This is because the first (outer) function call returns a second (inner) function. Only after we call the second function do we actually get the result. This is more evident if we separate the calls on two lines …
const add2 = add(2) // returns function(y) { return 2 + y }
add2(3) // returns 5
Applying our new understanding to your code
related: ”What’s the difference between binding, partial application, and currying?”
OK, now that we understand how that works, let’s look at your code
handleChange = field => e => {
e.preventDefault()
/// Do something here
}
We’ll start by representing it without using arrow functions …
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
};
};
However, because arrow functions lexically bind this
, it would actually look more like this …
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
}.bind(this)
}.bind(this)
Maybe now we can see what this is doing more clearly. The handleChange
function is creating a function for a specified field
. This is a handy React technique because you’re required to setup your own listeners on each input in order to update your applications state. By using the handleChange
function, we can eliminate all the duplicated code that would result in setting up change
listeners for each field. Cool!
1 Here I did not have to lexically bind this
because the original add
function does not use any context, so it is not important to preserve it in this case.
Even more arrows
More than two arrow functions can be sequenced, if necessary –
const three = a => b => c =>
a + b + c
const four = a => b => c => d =>
a + b + c + d
three (1) (2) (3) // 6
four (1) (2) (3) (4) // 10
Curried functions are capable of surprising things. Below we see $
defined as a curried function with two parameters, yet at the call site, it appears as though we can supply any number of arguments. Currying is the abstraction of arity –
const $ = x => k =>
$ (k (x))
const add = x => y =>
x + y
const mult = x => y =>
x * y
$ (1) // 1
(add (2)) // + 2 = 3
(mult (6)) // * 6 = 18
(console.log) // 18
$ (7) // 7
(add (1)) // + 1 = 8
(mult (8)) // * 8 = 64
(mult (2)) // * 2 = 128
(mult (2)) // * 2 = 256
(console.log) // 256
Partial application
Partial application is a related concept. It allows us to partially apply functions, similar to currying, except the function does not have to be defined in curried form –
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const add3 = (x, y, z) =>
x + y + z
partial (add3) (1, 2, 3) // 6
partial (add3, 1) (2, 3) // 6
partial (add3, 1, 2) (3) // 6
partial (add3, 1, 2, 3) () // 6
partial (add3, 1, 1, 1, 1) (1, 1, 1, 1, 1) // 3
Here’s a working demo of partial
you can play with in your own browser –
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const preventDefault = (f, event) =>
( event .preventDefault ()
, f (event)
)
const logKeypress = event =>
console .log (event.which)
document
.querySelector ('input[name=foo]')
.addEventListener ('keydown', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">