PostHole
Compose Login
You are browsing eu.zone1 in read-only mode. Log in to participate.
rss-bridge 2018-12-02T00:00:00+00:00

How Does React Tell a Class from a Function?

We talk about classes, new, instanceof, prototype chains, and API design.


How Does React Tell a Class from a Function?

December 2, 2018

Pay what you like

Consider this Greeting component which is defined as a function:

function Greeting() {
return <p>Hello</p>;

React also supports defining it as a class:

class Greeting extends React.Component {
render() {
return <p>Hello</p>;

(Until recently, that was the only way to use features like state.)

When you want to render a <Greeting />, you don’t care how it’s defined:

// Class or function — whatever.
<Greeting />

But React itself cares about the difference!

If Greeting is a function, React needs to call it:

// Your code
function Greeting() {
return <p>Hello</p>;

// Inside React
const result = Greeting(props); // <p>Hello</p>

But if Greeting is a class, React needs to instantiate it with the new operator and then call the render method on the just created instance:

// Your code
class Greeting extends React.Component {
render() {
return <p>Hello</p>;

// Inside React
const instance = new Greeting(props); // Greeting {}
const result = instance.render(); // <p>Hello</p>

In both cases React’s goal is to get the rendered node (in this example, <p>Hello</p>). But the exact steps depend on how Greeting is defined.

So how does React know if something is a class or a function?

Just like in my previous post, you don’t need to know this to be productive in React. 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.

This blog is for a curious reader who wants to know why React works in a certain way. Are you that person? Then let’s dig in together.

This is a long journey. Buckle up. This post doesn’t have much information about React itself, but we’ll go through some aspects of new, this, class, arrow functions, prototype, __proto__, instanceof, and how those things work together in JavaScript. Luckily, you don’t need to think about those as much when you use React. If you’re implementing React though…

(If you really just want to know the answer, scroll to the very end.)


First, we need to understand why it’s important to treat functions and classes differently. Note how we use the new operator when calling a class:

// If Greeting is a function
const result = Greeting(props); // <p>Hello</p>

// If Greeting is a class
const instance = new Greeting(props); // Greeting {}
const result = instance.render(); // <p>Hello</p>

Let’s get a rough sense of what the new operator does in JavaScript.


In the old days, JavaScript did not have classes. However, you could express a similar pattern to classes using plain functions. Concretely, you can use any function in a role similar to a class constructor by adding new before its call:

// Just a function
function Person(name) {
this.name = name;

var fred = new Person('Fred'); // ✅ Person {name: 'Fred'}
var george = Person('George'); // 🔴 Won’t work

You can still write code like this today! Try it in DevTools.

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.

By adding new before the call, we say: “Hey JavaScript, I know Person is just a function but let’s pretend it’s something like a class constructor. Create an {} object and point this inside the Person function to that object so I can assign stuff like this.name. Then give that object back to me.

That’s what the new operator does.

var fred = new Person('Fred'); // Same object as `this` inside `Person`

The new operator also makes anything we put on Person.prototype available on the fred object:

function Person(name) {
this.name = name;
Person.prototype.sayHi = function() {
alert('Hi, I am ' + this.name);

var fred = new Person('Fred');
fred.sayHi();

This is how people emulated classes before JavaScript added them directly.


So new has been around in JavaScript for a while. However, classes are more recent. They let us rewrite the code above to match our intent more closely:

class Person {
constructor(name) {
this.name = name;
sayHi() {
alert('Hi, I am ' + this.name);

let fred = new Person('Fred');
fred.sayHi();

Capturing developer’s intent is important in language and API design.

If you write a function, JavaScript can’t guess if it’s meant to be called like alert() or if it serves as a constructor like new Person(). Forgetting to specify new for a function like Person would lead to confusing behavior.

Class syntax lets us say: “This isn’t just a function — it’s a class and it has a constructor”. If you forget new when calling it, JavaScript will raise an error:

let fred = new Person('Fred');
// ✅  If Person is a function: works fine
// ✅  If Person is a class: works fine too

let george = Person('George'); // We forgot `new`
// 😳 If Person is a constructor-like function: confusing behavior
// 🔴 If Person is a class: fails immediately

This helps us catch mistakes early instead of waiting for some obscure bug like this.name being treated as window.name instead of george.name.

However, it means that React needs to put new before calling any class. It can’t just call it as a regular function, as JavaScript would treat it as an error!

class Counter extends React.Component {
render() {
return <p>Hello</p>;

// 🔴 React can't just do this:
const instance = Counter(props);

This spells trouble.


Before we see how React solves this, it’s important to remember most people using React use compilers like Babel to compile away modern features like classes for older browsers. So we need to consider compilers in our design.

In early versions of Babel, classes could be called without new. However, this was fixed — by generating some extra code:

function Person(name) {
// A bit simplified from Babel output:
if (!(this instanceof Person)) {
throw new TypeError("Cannot call a class as a function");
// Our code:
this.name = name;

new Person('Fred'); // ✅ Okay
Person('George');   // 🔴 Cannot call a class as a function

You might have seen code like this in your bundle. That’s what all those _classCallCheck functions do. (You can reduce the bundle size by opting into the “loose mode” with no checks but this might complicate your eventual transition to real native classes.)


By now, you should roughly understand the difference between calling something with new or without new:


|  | new Person() | Person() |
| --- | --- | --- |
| class | ✅ this is a Person instance | 🔴 TypeError |
| function | ✅ this is a Person instance | 😳 this is window or undefined |

This is why it’s important for React to call your component correctly. **If your component is defined as a class, React needs to use `new` when calling it.**

So can React just check if something is a class or not?

Not so easy! Even if we could [tell a class from a function in JavaScript](https://stackoverflow.com/questions/29093396/how-do-you-check-the-difference-between-an-ecmascript-6-class-and-function), this still wouldn’t work for classes processed by tools like Babel. To the browser, they’re just plain functions. Tough luck for React.

---

Okay, so maybe React could just use `new` on every call? Unfortunately, that doesn’t always work either.

With regular functions, calling them with `new` would give them an object instance as `this`. It’s desirable for functions written as constructor (like our `Person` above), but it would be confusing for function components:

[...]

---

*[Original source](https://overreacted.io/how-does-react-tell-a-class-from-a-function/)*
Reply