
How Does React Tell a Class from a Function? - obilgic
https://overreacted.io/how-does-react-tell-a-class-from-a-function/
======
arry
What's the rationale for allowing the components to be defined as functions in
the first place? As far as I can see, it has a single thing going for it:
saving a line of code, at the expense of jumping through the JavaScript hoops
to implement the class/function distinction described in the post. Besides,
when the function component evolves and acquires the need for state or other
features, the saved line of code goes right back in.

~~~
IMTDb
To me it's all about intent. By using a function instead of a class, you are
basically saying "future reader, this component is nothing more than a simple
mapping from those values to that DOM tree, don't try to look for internal
states or any complex treatment here".

Sure you could write a "normal React component without state" and do the same,
but having those properties baked in the way you defined the component is much
stronger.

~~~
quietbritishjim
I absolutely agree with you.

This makes me very skeptical about the proposed feature in React, called
"hooks", which is linked to in the article. It lets you add state to your
functional components, and it looks like you end up bundling all your code
into one function rather than having a separate constructor to initialise the
state (to me, separating out that function is a good thing). I would be
interested in the motivation for that.

Link: [https://reactjs.org/docs/hooks-
intro.html](https://reactjs.org/docs/hooks-intro.html)

~~~
sametmax
I have this doubt as well.

Hooks will allow people to use "class like" features and behavior, but with
none of the conventions forced by syntax, plus some pitfalls.

To me it seems a good way to ensure most devs (espacially unexperienced casual
ones, which are legions in JS by nature of the market) will dump their logic
wherever it works with as little structure as it allows. It's a technical debt
catalyser, and will make sure nobody feels at home in someone else's code. The
community will pay the price in a year.

It's also yet another way to do the same thing, and JS has already a lot of
those. React as well. Documentations, tutorials and examples will suffer from
it. But again, it's very common in JS land where learning anything requires
gathering scattered puzzle pieces then assembling them, hoping they are from
the same set this time. It allows me to bill more, so I'm not complaining, but
it's not fair to newcommers.

Note that I can see some nice things about hooks. I just strongly think they
don't outweight the cost by an order of magnitude.

FB idea of ergonomics and user friendliness, in API and UI, has always been to
force their way into things, until it blocks, back down, and fix things when
possible or use a workaround.

Having been training people in web tech for years, the consequences of such
policy show in the classroom. But, hey, more money for me :) Plus when I dev,
I have the experience to avoid the pain, mostly knowing when not to do or use
things. But I can tell than many of my younger colleagues don't, and a few of
my not so young ones too.

Still, I wish they gave a bit more though to this aspects of their products:
their are brillant technicians, so it's not that they can't, it's just a
matter of culture.

~~~
ergothus
> will dump their logic wherever it works with as little structure as it
> allows. It's a technical debt catalyser, and will make sure nobody feels at
> home in someone else's code. The community will pay the price in a year.

I don't want to downplay your concerns, but I will say that this is already a
problem. Part of the goal with hooks (and I can't say if it achieves that) is
to encourage the separation of a lot of state-interaction OUT of the
components. Today it is much too easy (arguably it is easiest) to write a
component that is basically untestable - basically a mini-application in
itself, which is the opposite of good React design.

I'm hopeful that Hooks will help, and I think the original demonstration
focused too much on state and not enough on other interactions external to the
component, which is where the real pain today exists. I've not played with the
alpha at all to have a feel for how realistic my hopes are.

~~~
nojvek
I read the hooks documentation. Why would someone find this pattern better
than a pre-defined state object ? I don’t get it.

React seems like it’s getting more complicated every single day. I loved it
due to its simplicity but now i’m not so sure.

~~~
nfRfqX5n
mostly because you can encapsulate and reuse some state logic. allows for
easier testing too.

~~~
sametmax
You could already do that with regular functions, class inheritence, mixin,
prototype inheritence, composition, event dispatching or stores.

If preventing people from being confused and doing the wrong thing was the
goal, I'm not sure adding one new way to the mix is going to make things
clearer.

Besides, while I agree tooling help with applying good practices, including
encapsulation, it's no substitute for a good initial API design.

React and JS API are confusing, because they have been designed this way from
the start. We are now adding things on top of it, again and again, without
ever fixing anything at the bottom. This creates problems at the same time it
solves others.

Another issue is that the JS community wants to professionalise without
aknowledging that a huge part of it doesn't have the skill to do so yet. It's
a community that includes many young devs with little experience, people that
are not programmers but ends up doing so, devs born in a culture of instant
results... It's important to address this as well, by talking to them in
tutorials and documentation, by creating API shaped with them in mind, so that
they can grow in empowering directions.

Instead, we create monster trucks, and they put ads on youtube to tell
everyone how "easy it is to drive" and "you should do it too right now".

For me, hooks are a monster truck. And most JS devs I work with are not
capable of driving that safely.

~~~
bennyg
Sure, but if you can drive hooks safely, they seems to make some things _very_
clean. I'm going back through and rewriting a toy app I haven't launched yet
to use hooks and it's been a joy to work with them so far.

~~~
fitpolar
This seems to be the response to every addition to javascript or react without
addressing the above concerns.

Nothing wrong with your comment on trying to embrace it per se, but it would
be healthier if this sentiment was balanced in the community with more
skepticism rather than blind embrace.

------
abhisuri97
"Please don’t turn this into an interview question. In fact, this post is more
about JavaScript than it is about React" I love this line.

~~~
ronilan
“In a December, 2nd, 2018 blog post, developer Dan Abramov explained in length
and in detail how React can (and/or can not) tell a Class from a Function. He
wrote: ‘please don’t turn this into an interview question’ Can you define what
_this_ refers to?”

~~~
tomjakubowski
I don't think I can answer that without knowing how this has been bound by the
caller.

------
xrd
I've read at least two dozen explanations of prototypes that didn't offer 10%
of the clarity of this post. And another brilliantly told story of how React
does its work. Bravo.

~~~
cryptonector
I agree, this is the best explanation of JavScript's
new/this/prototype/__proto__ I've seen yet. It was definitely worth the read.

~~~
baseballMan
Totally! I'm not even a full time JS dev (I just have played around with React
before) and I understood all of it.

------
Waterluvian
I'm really quite surprised how much time is spent worrying about Babel.

I always thought that you could just write valid JavaScript and it's
completely Babel's responsibility not to screw that up.

Edit: not sure what's wrong with my comment so let me clarify:

I wouldn't have thought that library developers had to think about what other
tools might do with their library. I always thought that so long as your code
is valid JavaScript, Babel would faithfully transpile it down to another valid
target using polyfills and whatnot.

~~~
sophiebits
(I work on React.) Most of our users use Babel so it would be irresponsible
for us to not consider how React and Babel play together; if we didn't we'd
quickly get into a situation where no one can use React because their setups
are incompatible.

For what it's worth, if we found while developing React that Babel was
compiling something in a problematic way and we had a better suggestion for
the compiled output, we'd certainly see if we could get it fixed upstream. In
this case the Babel output for classes is pretty reasonable so there's nothing
to change.

We did briefly consider if it made sense to require every function component
to be written using an arrow functions and asking Babel to delete the
.prototype property from every compiled arrow function (compiling _a = () = >
..._ into something like _a = function() { ... }; a.prototype = undefined;_ ).
We eventually decided that the .prototype.isReactComponent compromise
described in the post was a better result overall, both for authoring
ergonomics and runtime performance.

------
kbumsik
> I didn’t know this for years. Please don’t turn this into an interview
> question. In fact, this post is more about JavaScript than it is about
> React.

Well, I think how prototype works is much more important than using React.

I think having an interview in JS without knowing prototype is like having an
Java interview without knowing polymorphism, abstract class, interface or
something.

~~~
maceurt
I have heard Prototype in js used as a term, but I don't really understand
what it means. I have programmed a good deal of things in js also, and I have
never felt like I needed to explicitly know what Prototype means. Is it really
essential to know what this term means?

~~~
wallawe
As a js dev for 6 years now, and having conducted hundreds of interviews, I
would say it is not necessary for a junior developer and most of the things
they'd be doing.

However, when it comes to a mid or senior level engineer, it's important not
to just make things work, but to understand the how and why in order to build
performant applications at scale.

So to answer your question, no it's not a necessity to build applications.
Yes, it is if you want to excel at building applications.

------
ricardobeat
“In the old days, JavaScript did not have classes” gave me a laugh. This was
3-4 years ago.

~~~
Grue3
My reaction was "Wait, JavaScript _has_ classes now???"

~~~
Shorel
If you have to use .bind(), then no, it doesn't.

~~~
MoTTs_
In C++ you have to use bind too.

~~~
Shorel
No, not even close.

You can use it if you want to, but it has never been popular in C++, and in 20
years I have never encountered a problem that could be solved by using it, so
I have never used it. And in C++ bind is part of the functional programming
paradigm, as it does currying, not much to do with classes.

Bind in JavaScript makes your shoehorned collection of attributes actually
behave like objects. We can see JavaScript's bind used everywhere.

~~~
MoTTs_
The issue we complain about in JavaScript is one like this:

    
    
        class C {
            handleEvent() {}
    
            someMethod() {
                onEvent(this.handleEvent); // bad; "this" lost
                onEvent(this.handleEvent.bind(this); // ok
            }
        }
    

C++ requires the same thing. The bad case will even generate an error at
compile time.

    
    
        class C {
            public:
                void handle_event() {}
    
                void some_method() {
                    on_event(&C::handle_event); // bad; error
                    on_event(std::bind(&C::handle_event, this)); // ok
                }
        };

------
tcgv
TLDR Version: React defines the property "isReactComponent" in its "Component"
class prototype object. When you extend "Component" React can simply check
"MyClass.prototype.isReactComponent" and if the result is true, it knows it's
a class and not a function!

------
leeoniya
i've used this in the past:

    
    
      function isClass(fn) {
        return !Object.getOwnPropertyDescriptor(fn, 'prototype').writable;
      }
    

it relies on the fact that the prototype object of ES6 classes always non-
writable.

~~~
alangpierce
Note that this breaks with Babel and TypeScript, since I guess this is one
aspect where neither strictly follows the spec. Both of these examples print
false:

[https://babeljs.io/repl/build/master/#?babili=false&browsers...](https://babeljs.io/repl/build/master/#?babili=false&browsers=&build=&builtIns=false&code_lz=GYVwdgxgLglg9mABDAzgYQDYEMUoBTBgCUiA3gFCKIBOAplCNUgIQDyARgFa3QB0A5vVYB3MAAVqcAA61qUAJ4ARWigjUYUqHGoEwAGkQByKZK0KZhor2HqoWdhloBucgF9y5CNlyIAgmXdPBBQ4R14MOH48VEwcfF8iIhcgA&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&lineWrap=true&presets=es2015%2Creact&prettier=false&targets=&version=7.1.6&envVersion=)

[https://www.typescriptlang.org/play/index.html#src=function%...](https://www.typescriptlang.org/play/index.html#src=function%20isClass\(fn\)%20%7B%0A%20%20return%20!Object.getOwnPropertyDescriptor\(fn%2C%20'prototype'\).writable%3B%0A%7D%0A%0Aclass%20A%20%7B%7D%0A%0Aconsole.log\(isClass\(A\)\)%3B%0A)

------
iooi
On a related note, this is the one article which after reading I wanted to
subscribe to, but could not find a newsletter or subscription. Dan, if you're
reading this, this might help: [https://www.gatsbyjs.org/packages/gatsby-
plugin-mailchimp/?n...](https://www.gatsbyjs.org/packages/gatsby-plugin-
mailchimp/?no-cache=1)

I'm guessing the site is built with gatsby from the theme.

~~~
rexpan
Yup, it is gatsby.
[https://github.com/gaearon/overreacted.io](https://github.com/gaearon/overreacted.io)

You can use [https://overreacted.io/rss.xml](https://overreacted.io/rss.xml)
for subscription.

------
petilon
So React added isReactComponent to Component class but the author doesn't
explain why they couldn't just check for render() method. It works in this
React-like library:

[https://github.com/wisercoder/uibuilder/blob/master/UIBuilde...](https://github.com/wisercoder/uibuilder/blob/master/UIBuilder/UIBuilder.ts#L130)

~~~
acemarke
Someone else asked that same question on Reddit [0], and Dan pointed out the
paragraph in the post that answers it :

> One other possible heuristic could be to check for presence of a render
> method on the prototype. However, at the time it wasn’t clear how the
> component API would evolve. Every check has a cost so we wouldn’t want to
> add more than one. This would also not work if render was defined as an
> instance method, such as with the class property syntax.

[0]
[https://www.reddit.com/r/reactjs/comments/a2j6xk/how_does_re...](https://www.reddit.com/r/reactjs/comments/a2j6xk/how_does_react_tell_a_class_from_a_function/eayzfhx/)

------
Kiro
> In the old days, JavaScript did not have classes.

Feels like yesterday to me. I still do my objects prototype style because
classes feel too new and bleeding edge.

------
cj
> Please don’t turn this into an interview question. In fact, this post is
> more about JavaScript than it is about React.

Aren't most people hiring frontend developers looking for people who
understanding Javascript (and not just React)?

Or is "React" a skill separate from Javascript these days?

Personally, I'd be hesitant to hire anyone who "knows React" but doesn't know
the basics of how it works.

~~~
zwerdlds
All of the hiring managers I've talked to recently have the JQuery/JS
dichotomy applied to React now. That is, they don't seem to really care that
there's a difference. You can say you're good with JS, Vue, DOM etc, but if
you don't check that React box, it's not good enough.

------
pier25
I recently discovered [http://imba.io/](http://imba.io/) which makes the
virtual DOM approach irrelevant. It just renders "everything, every time" like
the main loop of a game, so you don't need any more reconciliation, async
state management, etc.

In some benchmarks this approach is 20 to 30 times faster than React.

------
code001
Great job Dan, please keep posting more articles on your blog!

------
finchisko
I'm pretty much aware about prototype inheritance in js. But still scratching
my head everytime, I try to understand difference between prototype and
__proto__. Thankfully with ES6 classes and class inheritance constructs, we
don't need to override prototype constructor and such a things anymore. Still
wish, that somebody will explain it to me in plain english.

~~~
sophiebits
This post does try to explain it in plain English. See the explanation
starting at the bolded sentence that starts "Confusingly,".

~~~
ricardobeat
Honestly I don’t think it does a good job at explaining it. Classes are mostly
syntax sugar for “old” constructors and prototypes, not the other way around
(that’s the whole reason you can’t tell them apart). Would be easier to
explain it that way; on mobile right now so can’t comment too much.

~~~
jrumbut
I thought it was unfortunate that prototypes were characterized as an
inadequate approximation of classes, when in fact prototypes are a wholly
legitimate, powerful, and certainly interesting way to approach object-
oriented programming ([https://en.m.wikipedia.org/wiki/Prototype-
based_programming](https://en.m.wikipedia.org/wiki/Prototype-
based_programming)).

20+ years of confused programmers suggests that maybe it wasn't the right way
to go for a language that is frequently used by beginners, but still that
wasn't the fault of prototype based programming itself.

The new class system is syntactic sugar leveraging the power of prototypes.
This was always possible, and nothing stopped you from implementing a class-
like system in JavaScript a long time ago.

More, very readable, info here: [https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Guid...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model)

------
chairmanmow
React doesn't care whether something is a class or function until Congress
notices.

------
miklax
I like a domain name.

------
snek
mfw __proto__ instead of Object.getPrototypeOf

------
danschumann
TL;DR -- it uses `instanceof`

~~~
awestroke
Wrong, try again.

------
torgian
Ugh. This just doesn’t make me want to touch JavaScript. I’d rather stick to
Django and Python

~~~
Vinnl
I'm quite sure the authors of Django would be able to tell similar stories
about Python...

~~~
danabramov
Yeah that's kind of the point of this blog. I dive a bit deeper than you need
to know as an app developer. Also touched on this at the end:

>If the final API is successful, its users never have to think about this
process. Instead they can focus on creating apps.

~~~
perfunctory
> Instead they can focus on creating apps.

If only it worked that way. Unfortunately, somehow I always end up needing to
know about the process cause the abstraction leaks like a sieve. And it's
leakier in javascript than in any other language.

------
galaxyLogic
You get an error if you try to call an ES6 class as a function (without
'new'). So just try to do that inside a try-catch. If there is no error you
know it is not a class and you have the result of the function already. If you
do get an error and you know it is a function you know it must be a class.

~~~
alangpierce
I believe throwing and catching exceptions over and over as part of normal
rendering is the sort of thing that would be disastrous for performance,
certainly the sort of thing that the React authors would really want to avoid.

~~~
sorahn
The new suspense api is based on throwing and catching promises as part of the
render, so I don’t think its that.

~~~
danabramov
Suspense only uses throwing when it's blocked on network. There is huge
difference between doing this once every few seconds, and doing this thousands
of times per second.

------
wz1000
I have written maybe 200 lines of JavaScript in my life, and this article has
strongly reinforced my desire to never have to write a single one more. The
next time someone asks me what the problem with JavaScript is, I will send
them this article. "Scheme for the browser" what a joke.

Some excerpts for those that didn't have the strength to sit through the
entire thing without pulling their hair out:

> If you called Person('Fred') without new, this inside it would point to
> something global and useless (for example, window or undefined). So our code
> would crash or do something silly like setting window.name.

> However, JavaScript also allows a function called with new to override the
> return value of new by returning some other object. Presumably, this was
> considered useful for patterns like pooling where we want to reuse
> instances:

> However, new also completely ignores a function’s return value if it’s not
> an object. If you return a string or a number, it’s like there was no return
> at all.

> And yet I still find it very confusing that a property called prototype does
> not give you a value’s prototype (for example, fred.prototype is undefined
> because fred is not a function). Personally, I think this is the biggest
> reason even experienced developers tend to misunderstand JavaScript
> prototypes.

> However, some class implementations we wanted to target did not copy static
> properties (or set the non-standard __proto__), so the flag was getting lost

Edit: And I missed the best/worst one yet: You can have MULTIPLE VERSIONS of
the SAME DEPENDENCY running around in your codebase, interacting in ways that
only god knows. In retrospect, this should have been obvious from the way
javascript "imports" work, but, holy shit! If Satan himself had to design a
language, he could not have done a better job. And I'm sure he would not have
the stroke of genius required to make it the only programming language you can
use if you want to target the most widely available platform for code in the
world.

The next time I have to allow NoScript to let some js in, I will wonder how
many goat sacrifices went into making the thing work.

~~~
danabramov
Author here :-) I don't think I disagree with you. JS has its warts and some
of them are... weird. The web delivery mechanism has always been its killer
feature, not the language. And for web delivery mechanism to work, it needs to
maintain backwards compatibility.

In practice, however, I think a lot of these things become irrelevant because
JS subcommunities carve out reasonable subsets of the language. For example
when using React you almost never need to care about prototypal (or even
normal) inheritance because it's explicitly discouraged by React. And in the
future, we might not need to encourage classes when writing components
altogether.

If you limit yourself to avoiding some features then it's not such a bad
language after all.

~~~
perfunctory
> If you limit yourself to avoiding some features then it's not such a bad
> language after all.

Unfortunately that's not possible unless you never interoperate with other
code.

