I got an interesting question recently: "wtf is the double arrow?" (It was not phrased exactly like this tho.) Here it is in action in file App.js:
removeNoteFromList = (noteId) => () => { // wtf
// more work here
}
So:
That is a great question. Well, let's find out. We know that =>
is an ES6 trickery, so if you do not remember the syntax we can try to transpile it to something more familiar. https://babeljs.io/repl/ is a nice tool! Paste our code to the left box and observe the right one.
The output is:
var removeNoteFromList = function removeNoteFromList(noteId) {
return function () {
// more work here
};
};
Aha! A function that returns a function. Now naturally the next question becomes "but why?" which is an excellent question because all of this is kind of difficult to get used to unless you did some Haskell in the past.
The difference is when you use the function. Let's compare it to a simple arrow function in this example:
class Example extends React.Component {
// defining ...
removeSingle = noteId => {
// work
}
// defining ...
removeDouble = noteId => () => {
// work
}
// let's try to use them functions!
someOtherFunction = () => {
// nice and easy
removeSingle(1)
// omg so many braces
const remover = removeDouble(1)
remover()
// or without the intermediate variable - which one you prefer?
removeDouble(1)()
}
}
What happened here? Having the nested function actually made things worse! But wait, there is more. If we wish to pass the function as an event handler, things turn the other way around:
render() {
const remover = this.removeSingle.bind(this, noteId)
return (
<div>
{/* bound function */}
<button onClick={remover}>
{/* ... or anonymous function; fat arrow binds `this` */}
<button onClick={(e) => { this.removeSingle(noteId) }}>
{/* but! */}
<button onClick={this.removeDouble(noteId)}>
</div>
)
}
See? Function that returns function made it easier to use in event handlers. Which happens a lot in frontend code; you only want to hook event handlers (some call them event listeners) and wait for user action.
Some more reading:
This concept is somehow but not exactly related to currying (homework: why?). Now that you understand the concept of functions returning functions you may check it out too:
- M. David Green: Currying in Functional JavaScript
- Alex Rauschmayer (2ality): Currying versus partial application
This is not directly related to the original question but I want to include it here because it is important to understand the most common problem with classes in JS: this
.
When using classes you need to be careful what exactly is this
in your code. It may or may not be what you expect. See this code:
class ThisIsHorrible {
// defined as a oldschool method
brokenFn() {
console.log(this, this.javascript)
// Throws a TypeError: this.setState is not a function because `this` is not what you expect.
// (actually a global object: Window)
this.setState({ output: this.javascript })
}
// defined with equals = and fat arrow =>
workingFn = () => {
console.log(this, this.javascript)
// Works as expected because `this` is an instance of `ThisIsHorrible` Component
// In other words: `this` is bound
this.setState({ output: this.javascript })
}
}
(I have omitted some code for clarity. See the full running example on codepen.io if you're interested.)
For complete mastery and understanding of various rules for this
I recommend the excellent book You Don't Know JS: this & Object Prototypes.
This is even less related but I also want to include it here because you may wonder why none of these examples actually work when you copy and paste them in your browser console or run it without transpiling first.
This is because the "equals" definition is not standard and workingFn =
throws a SyntaxError. It is actually a Candidate (Stage 3) proposal which means it may or may not become part of the language. You will need babel to transpile it.
If you wish to learn more about the ES Stages see the official document and explanation by Alex Rauschmayer.
Did I say that JavaScript is simple? I most certainly did not.