This here:
useEffect(()=>{
const sub = interval(10).subscribe();
return () => sub.unsubscribe();
});
could be re-written as:
useEffect(()=>{
const sub = interval(10).subscribe();
function unsub(){
sub.unsubscribe();
}
return unsub;
});
The key thing to notice is that you're returning a function back to React. unsub isn't called right away, it's called later when the component unmounts.
In fact, you can return arbitrary code to be run later:
useEffect(() => {
/******
* Code that gets called when
* effect is run
******/
return () => { // <- start of cleanup function
/******
* Code that gets called to
* clean up the effect later
******/
} // <- end of cleanup function
});
The problem
I'll rewrite your solution to make talking about the problem clearer. This is semantically equivalent, I've just introduced an intermediate variable.
useEffect(() =>
const sub = interval(10).subscribe();
return sub.unsubscribe;
);
The the question most clearly boils down to: What are the differences between these values? Under which circumstances (if any) will one fail while the other does not.
sub.unsubscribe
() => sub.unsubscribe()
If unsubscribe is a function (isn't bound to an instance of a class/object because it doesn't contain the this keyword), then the two are semantically equivalent.
The issue is that unsubscribe is not actually a function. It's a method on an subscription object. Because of this, the first value above is an unbound method where this is undefined. The moment the method attempts to use its context (this), JavaScript will throw an error.
To make sure that unsubscribe gets called as a method you could do this:
useEffect(() => {
const sub = interval(10).subscribe();
return sub.unsubscribe.bind(sub);
});
You have one less level of indirection this way, though it looks roughly the same.
Furthermore, I would recommend against using bind in most cases. Methods, functions, anonymous lambda functions, and attributes containing any of these three as values all behave differently on various edge cases.
As far as I know, () => a.b() may be needlessly wrapping a function, but will not fail. Plus JIT will optimize this fairly well 99.9% of cases.
Where a.b.bind(a) will fail on a previously bound method, but be optimized 100% of the time. I wouldn't use bind unless it's necessary (and it rarely is)
Update:
Just a quick aside: I use function here to denote a callable block of code which doesn't rely on a context (Doesn't have an object that it references using the this keyword) and a method to denote a callable block of code that DOES rely on some context.
If you prefer other terminology, that's fine. Swap out the words as you read them, I won't take offense, promise :)