Senin, 15 Juni 2020

My Wishlist for Hot Reloading

Do you have a project that you approach repeatedly with a mix of success and failure, step aside for a while, and then try again — year after year? For some, it might be a router or a virtual list scroller. For me, it’s hot reloading.

My first exposure to the idea of changing code on the fly was a brief mention in a book about Erlang that I read as a teenager. Much later, like many others, I fell in love with Bret Victor’s beautiful demos. I’ve read somewhere Bret was unhappy with people cherry-picking “easy” parts of his demos and screwing up the big vision. (I don’t know if this is true.) In either case, to me shipping even small incremental improvements that people take for granted later is a success. Smarter people than me will work on Next Big Ideas.

Now, I want to be clear that none of the ideas discussed in this post are mine. I’ve been inspired by many projects and people. In fact, even people whose projects I’ve never tried occasionally told me I’ve ripped off their stuff.

I’m not an inventor. If I have a “principle”, it is to take a vision that inspires me, and share it with more people — through words, code, and demos.

And hot reloading inspires me.


I’ve taken several attempts at implementing hot reloading for React.

In retrospect, the first demo I cobbled together changed my life. It got me my first Twitter followers, first thousand GitHub stars, later first HN frontpage hit, and even my first conference talk (bringing Redux into existence, oops). This first iteration worked fairly well. However, soon React moved away from createClass, making a reliable implementation much more difficult.

Since then I’ve done a few more attempts to fix it, each flawed in a different way. One of them is still being used in React Native (hot reloading functions doesn’t work there because of my mistakes — sorry!)

Frustrated with my inability to work around some issues and the lack of time, I handed React Hot Loader over to a few talented contributors. They have been pushing it forward and found clever workarounds for my design flaws. I am grateful to them for keeping the project in a good state despite the challenges.


To be clear, hot reloading in React is quite usable today. In fact, this blog uses Gatsby which uses React Hot Loader under the hood. I save this post in my editor and it updates without refreshing. Magic! In some ways, the vision that I worried wouldn’t ever see mainstream usage is already almost boring.

But there are plenty of people who feel it isn’t as good as it could be. Some dismiss it as a gimmick and that breaks my heart a little bit, but I think what they’re really saying is: the experience is not seamless. It’s not worth it if you’re never sure whether a hot reload worked, if it breaks in confusing ways, or if it’s easier to just refresh. I agree with this 100%, but to me it means we have more work to do. And I’m excited to start thinking about what official React support for hot reloading could look like in the future.

(If you use a language like Elm, Reason or ClojureScript, maybe those problems are already solved in your ecosystem. I’m happy that you’re happy. This won’t stop me from trying and failing to bring good stuff to JavaScript.)


I think I’m ready to take another attempt at implementing it. Here’s why.

Ever since createClass stopped being the primary way we define components, the biggest source of complexity and fragility in hot reloading components was dynamically replacing class methods. How do you patch existing instances of classes with new “versions” of their methods? The simple answer is “replace them on the prototype” but even with Proxies, in my experience there are too many gnarly edge cases for this to work reliably.

By comparison, hot reloading functions is easy. A Babel plugin could split any function component exported from a module into two functions:

// Reassigns the latest version
window.latest_Button = function(props) {
  // Your actual code is moved here by a plugin
  return <button>Hello</button>;
}

// Think of this as a "proxy"
// that other components would use
export default function Button(props) {
  // Always points to latest version
  return window.latest_Button(props);
}

Every time this module re-executes after an edit, window.latest_Button would point to the latest implementation. Reusing the same Button function between module evaluations would let us trick React into not unmounting our component even though we swapped out the implementation.

For a long time, it seemed to me that implementing reliable hot reloading for functions alone would encourage people to write convoluted code just to avoid using classes. But with Hooks, function components are fully featured so this is not a concern anymore. And this approach “just works” with Hooks:

// Reassigns the latest version
window.latest_Button = function(props) {
  // Your actual code is moved here by a plugin
  const [name, setName] = useState('Mary');
  const handleChange = e => setName(e.target.value);
  return (
    <>
      <input value={name} onChange={handleChange} />
      <h1>Hello, {name}</h1>
    </>
  );
}

// Think of this as a "proxy"
// that other components would use
export default function Button(props) {
  // Always points to latest version
  return window.latest_Button(props);
}

As long as the Hook call order doesn’t change, we can preserve the state even as window.latest_Button is replaced between file edits. And replacing event handlers “just works” too — because Hooks rely on closures, and we replace the whole function.


This was just a rough sketch of one possible approach. There are more (some are very different). How do we evaluate and compare them?

Before I get too attached to a specific approach that might be flawed in some way, I decided to write down a few principles that I think are important for judging any hot reloading implementation for component code.

It would be nice to express some of these principles as tests later. These rules aren’t strict and there might be reasonable compromises. But if we decide to break them, that should be an explicit design decision and not something we accidentally discover later.

Here goes my wish list for hot reloading React components:

Correctness

  • Hot reloading should be unobservable before the first edit. Until you save a file, the code should behave exactly as it would if hot reloading was disabled. It’s expected that things like fn.toString() don’t match, which is already the case with minification. But it shouldn’t break reasonable application and library logic.

  • Hot reload shouldn’t break React rules. Components shouldn’t get their lifecycles called in an unexpected way, accidentally swap state between unrelated trees, or do other non-Reacty things.

  • Element type should always match the expected type. Some approaches wrap component types but this can break <MyThing />.type === MyThing. This is a common source of bugs and should not happen.

  • It should be easy to support all React types. lazy, memo, forwardRef — they should all be supported and it shouldn’t be hard to add support for more. Nested variations like memo(memo(...)) should also work. We should always remount when the type shape changes.

  • It shouldn’t reimplement a non-trivial chunk of React. It’s hard to keep up with React. If a solution reimplements React it poses problems in longer term as React adds features like Suspense.

  • Re-exports shouldn’t break. If a component re-exports components from other modules (whether own or from node_modules), that shouldn’t cause issues.

  • Static fields shouldn’t break. If you define a ProfilePage.onEnter method, you’d expect an importing module to be able to read it. Sometimes libraries rely on this so it’s important that it’s possible to read and write static properties, and for component itself to “see” the same values on itself.

  • It is better to lose local state than to behave incorrectly. If we can’t reliably patch something (for example, a class), it is better to lose its local state than to do a mixed success effort at updating it. The developer will be suspicious anyway and likely force a refresh. We should be intentional about which cases we’re confident we can handle, and discard the rest.

  • It is better to lose local state than use an old version. This is a more specific variation of the previous principle. For example, if a class couldn’t be hot reloaded, the code should force a remount for those components with the new version rather than keep rendering a zombie.

Locality

  • Editing a module should re-execute as few modules as possible. Side effects during component module initialization are generally discouraged. But the more code you execute, the more likely something will cause a mess when called twice. We’re writing JavaScript, and React components are islands of (relative) purity but even there we don’t have strong guarantees. So if I edit a module, my hot reloading solution should re-execute that module and try to stop there if possible.

  • Editing a component shouldn’t destroy the state of its parents or siblings. Similar to how setState() only affects the tree below, editing a component shouldn’t affect anything above it.

  • Edits to non-React code should propagate upwards. If you edit a file with constants or pure functions that’s imported from several components, those components should update. It is acceptable to lose module state in such files.

  • A runtime error introduced during hot reloading should not propagate. If you make a mistake in one component, it shouldn’t break your whole app. In React, this is usually solved by error boundaries. However, they are too coarse for the countless typos we make while editing. I should be able to make and fix runtime errors while I work on a component without its siblings or parents unmounting. However, errors that don’t happen during hot reload (and are legitimate bugs in my code) should go to the closest error boundary.

  • Preserve own state unless it’s clear the developer doesn’t want to. If you’re just tweaking styles, it’s frustrating for the state to reset on every edit. On the other hand, if you just changed the state shape or the initial state, you’ll often prefer it to reset. By default we should try our best to preserve state. But if it leads to an error during hot reload, this is often a sign some assumption has changed, so we should reset state and retry rendering in that case. Commenting things out and back in is common so it’s important to handle that gracefully. For example, removing Hooks at the end shouldn’t reset state.

  • Discard state when it’s clear the developer wants to. In some cases we can also proactively detect that the user wants to reset. For example, if the Hook order changed, or if primitive Hooks like useState change their initial state type. We can also offer a lightweight annotation that you can use to force a component to reset on every edit. Such as // ! or some similar convention that’s fast to add and remove while you focus on how component mounts.

  • Support updating “fixed” things. If a component is wrapped in memo(), hot reload should still update it. If an effect is called with [], it should still be replaced. Code is like an invisible variable. Previously, I thought it was important to force deep updates below for things like renderRow={this.renderRow}. But in the Hooks world, we rely on closures anyway this seems unnecessary anymore. A different reference should be sufficient.

  • Support multiple components in one file. It is a common pattern that multiple components are defined in the same file. Even if we only keep the state for function components, we want to make sure putting them in one file doesn’t cause them to lose state. Note these can be mutually recursive.

  • When possible, preserve the state of children. If you edit a component, it’s always frustrating if its children unintentionally lose state. As long as the element types of children are defined in other files, we expect their state to be preserved. If they’re in the same file, we should do our best effort.

  • Support custom Hooks. For well-written custom Hooks (some cases like useInterval() can be a bit tricky), hot reloading any arguments (including functions) should work. This shouldn’t need extra work and follows from the design of Hooks. Our solution just shouldn’t get in the way.

  • Support render props. This usually doesn’t pose problems but it’s worth verifying they work and get updated as expected.

  • Support higher-order components. Wrapping export into a higher-order component like connect shouldn’t break hot reloading or state preservation. If you use a component created from a HOC in JSX (such as styled), and that component is a class, it’s expected that it loses state when instantiated in the edited file. But a HOC that returns a function component (potentially using Hooks) shouldn’t lose state even if it’s defined in the same file. In fact, even edits to its arguments (e.g. mapStateToProps) should be reflected.

Feedback

  • Both success and failure should provide visual feedback. You should always be confident whether a hot reload succeeded or failed. In case of a runtime or a syntax error you should see an overlay which should be automatically be dismissed after it is irrelevant. When hot reload is successful, there should be some visual feedback such as flashing updated components or a notification.

  • A syntax error shouldn’t cause a runtime error or a refresh. When you edit the code and you have a syntax error, it should be shown in a modal overlay (ideally, with a click-through to the editor). If you make another syntax error, the existing overlay is updated. Hot reloading is only attempted after you fix your syntax errors. Syntax error shouldn’t make you lose the state.

  • A syntax error after reload should still be visible. If you see a modal syntax error overlay and refresh, you should still be seeing it. It categorically should not let you run the last successful version (I’ve seen that in some setups).

  • Consider exposing power user tools. With hot reloading, code itself can be your “terminal”. In addition to the hypothetical // ! command to force remount, there could be e.g. an // inspect command that shows a panel with props values next to the component. Be creative!

  • Minimize the noise. DevTools and warning messages shouldn’t expose that we’re doing something special. Avoid breaking displayNames or adding useless wrappers to the debug output.

  • Debugging in major browsers should show the most recent code. While this doesn’t exactly depend on us, we should do our best to ensure the browser debugger shows the most recent version of any file and that breakpoints work as expected.

  • Optimize for fast iteration, not long refactoring. This is JavaScript, not Elm. Any long-running series of edits likely won’t hot reload well due to a bunch of mistakes that need to be fixed one by one. When in doubt, optimize for the use case of tweaking a few components in a tight iteration loop rather than for a big refactor. And be predictable. Keep in mind that if you lose developer’s trust they’ll refresh anyway.


This was my wish list for how hot reloading in React — or any component system that offers more than templates — should work. There’s probably more stuff I will add here with time.

I don’t know how many of these goals we can satisfy with JavaScript. But there’s one more reason I’m looking forward to working on hot reloading again. As an engineer I’m more organized than before. In particular, I’ve finally learned my lesson to write up requirements like this before diving into another implementation.

Maybe this one will actually work! But if it doesn’t, at least I’ve left some breadcrumbs for the next person who tries it.

Why Do React Elements Have a $$typeof Property?

You might think you’re writing JSX:

<marquee bgcolor="#ffa7c4">hi</marquee>

But really, you’re calling a function:

React.createElement(
  /* type */ 'marquee',
  /* props */ { bgcolor: '#ffa7c4' },
  /* children */ 'hi'
)

And that function gives you back an object. We call this object a React element. It tells React what to render next. Your components return a tree of them.

{
  type: 'marquee',
  props: {
    bgcolor: '#ffa7c4',
    children: 'hi',
  },
  key: null,
  ref: null,
  $$typeof: Symbol.for('react.element'), // 🧐 Who dis
}

If you used React you might be familiar with type, props, key, and ref fields. But what is $$typeof? And why does it have a Symbol() as a value?

That’s another one of those things that you don’t need to know to use React, but that will make you feel good when you do. There’s also some tips about security in this post that you might want to know. Maybe one day you’ll write your own UI library and all of this will come in handy. I certainly hope so.


Before client-side UI libraries became common and added basic protection, it was common for app code to construct HTML and insert it into the DOM:

const messageEl = document.getElementById('message');
messageEl.innerHTML = '<p>' + message.text + '</p>';

That works fine, except when your message.text is something like '<img src onerror="stealYourPassword()">'. You don’t want things written by strangers to appear verbatim in your app’s rendered HTML.

(Fun fact: if you only do client-side rendering, a <script> tag here wouldn’t let you run JavaScript. But don’t let this lull you into a false sense of security.)

To protect against such attacks, you can use safe APIs like document.createTextNode() or textContent that only deal with text. You can also preemptively “escape” inputs by replacing potentially dangerous characters like <, > and others in any user-provided text.

Still, the cost of a mistake is high and it’s a hassle to remember it every time you interpolate a user-written string into your output. This is why modern libraries like React escape text content for strings by default:

<p>
  {message.text}
</p>

If message.text is a malicious string with an <img> or another tag, it won’t turn into a real <img> tag. React will escape the content and then insert it into the DOM. So instead of seeing the <img> tag you’ll just see its markup.

To render arbitrary HTML inside a React element, you have to write dangerouslySetInnerHTML={{ __html: message.text }}. The fact that it’s clumsy to write is a feature. It’s meant to be highly visible so that you can catch it in code reviews and codebase audits.


Does it mean React is entirely safe from injection attacks? No. HTML and DOM offer plenty of attack surface that is too difficult or slow for React or other UI libraries to mitigate against. Most of the remaining attack vectors involve attributes. For example, if you render <a href={user.website}>, beware of the user whose website is 'javascript: stealYourPassword()'. Spreading user input like <div {...userData}> is rare but also dangerous.

React could provide more protection over time but in many cases these are consequences of server issues that should be fixed there anyway.

Still, escaping text content is a reasonable first line of defence that catches a lot of potential attacks. Isn’t it nice to know that code like this is safe?

// Escaped automatically
<p>
  {message.text}
</p>

Well, that wasn’t always true either. And that’s where $$typeof comes in.


React elements are plain objects by design:

{
  type: 'marquee',
  props: {
    bgcolor: '#ffa7c4',
    children: 'hi',
  },
  key: null,
  ref: null,
  $$typeof: Symbol.for('react.element'),
}

While normally you create them with React.createElement(), it is not required. There are valid use cases for React to support plain element objects written like I just did above. Of course, you probably wouldn’t want to write them like this — but this can be useful for an optimizing compiler, passing UI elements between workers, or for decoupling JSX from the React package.

However, if your server has a hole that lets the user store an arbitrary JSON object while the client code expects a string, this could become a problem:

// Server could have a hole that lets user store JSON
let expectedTextButGotJSON = {
  type: 'div',
  props: {
    dangerouslySetInnerHTML: {
      __html: '/* put your exploit here */'
    },
  },
  // ...
};
let message = { text: expectedTextButGotJSON };

// Dangerous in React 0.13
<p>
  {message.text}
</p>

In that case, React 0.13 would be vulnerable to an XSS attack. To clarify, again, this attack depends on an existing server hole. Still, React could do a better job of protecting people against it. And starting with React 0.14, it does.

The fix in React 0.14 was to tag every React element with a Symbol:

{
  type: 'marquee',
  props: {
    bgcolor: '#ffa7c4',
    children: 'hi',
  },
  key: null,
  ref: null,
  $$typeof: Symbol.for('react.element'),
}

This works because you can’t just put Symbols in JSON. So even if the server has a security hole and returns JSON instead of text, that JSON can’t include Symbol.for('react.element'). React will check element.$$typeof, and will refuse to process the element if it’s missing or invalid.

The nice thing about using Symbol.for() specifically is that Symbols are global between environments like iframes and workers. So this fix doesn’t prevent passing trusted elements between different parts of the app even in more exotic conditions. Similarly, even if there are multiple copies of React on the page, they can still “agree” on the valid $$typeof value.


What about the browsers that don’t support Symbols?

Alas, they don’t get this extra protection. React still includes the $$typeof field on the element for consistency, but it’s set to a number0xeac7.

Why this number specifically? 0xeac7 kinda looks like “React”.

How Does React Tell a Class from a Function?

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, 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:

function Greeting() {
  // We wouldn’t expect `this` to be any kind of instance here
  return <p>Hello</p>;
}

That could be tolerable though. There are two other reasons that kill this idea.


The first reason why always using new wouldn’t work is that for native arrow functions (not the ones compiled by Babel), calling with new throws an error:

const Greeting = () => <p>Hello</p>;
new Greeting(); // ðŸ”ī Greeting is not a constructor

This behavior is intentional and follows from the design of arrow functions. One of the main perks of arrow functions is that they don’t have their own this value — instead, this is resolved from the closest regular function:

class Friends extends React.Component {
  render() {
    const friends = this.props.friends;
    return friends.map(friend =>
      <Friend
        // `this` is resolved from the `render` method
        size={this.props.size}
        name={friend.name}
        key={friend.id}
      />
    );
  }
}

Okay, so arrow functions don’t have their own this. But that means they would be entirely useless as constructors!

const Person = (name) => {
  // ðŸ”ī This wouldn’t make sense!
  this.name = name;
}

Therefore, JavaScript disallows calling an arrow function with new. If you do it, you probably made a mistake anyway, and it’s best to tell you early. This is similar to how JavaScript doesn’t let you call a class without new.

This is nice but it also foils our plan. React can’t just call new on everything because it would break arrow functions! We could try detecting arrow functions specifically by their lack of prototype, and not new just them:

(() => {}).prototype // undefined
(function() {}).prototype // {constructor: f}

But this wouldn’t work for functions compiled with Babel. This might not be a big deal, but there is another reason that makes this approach a dead end.


Another reason we can’t always use new is that it would preclude React from supporting components that return strings or other primitive types.

function Greeting() {
  return 'Hello';
}

Greeting(); // ✅ 'Hello'
new Greeting(); // ðŸ˜ģ Greeting {}

This, again, has to do with the quirks of the new operator design. As we saw earlier, new tells the JavaScript engine to create an object, make that object this inside the function, and later give us that object as a result of new.

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:

// Created lazily
var zeroVector = null;

function Vector(x, y) {
  if (x === 0 && y === 0) {
    if (zeroVector !== null) {
      // Reuse the same instance
      return zeroVector;
    }
    zeroVector = this;
  }
  this.x = x;
  this.y = y;
}

var a = new Vector(1, 1);
var b = new Vector(0, 0);
var c = new Vector(0, 0); // ðŸ˜ē b === c

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.

function Answer() {
  return 42;
}

Answer(); // ✅ 42
new Answer(); // ðŸ˜ģ Answer {}

There is just no way to read a primitive return value (like a number or a string) from a function when calling it with new. So if React always used new, it would be unable to add support components that return strings!

That’s unacceptable so we need to compromise.


What did we learn so far? React needs to call classes (including Babel output) with new but it needs to call regular functions or arrow functions (including Babel output) without new. And there is no reliable way to distinguish them.

If we can’t solve a general problem, can we solve a more specific one?

When you define a component as a class, you’ll likely want to extend React.Component for built-in methods like this.setState(). Rather than try to detect all classes, can we detect only React.Component descendants?

Spoiler: this is exactly what React does.


Perhaps, the idiomatic way to check if Greeting is a React component class is by testing if Greeting.prototype instanceof React.Component:

class A {}
class B extends A {}

console.log(B.prototype instanceof A); // true

I know what you’re thinking. What just happened here?! To answer this, we need to understand JavaScript prototypes.

You might be familiar with the “prototype chain”. Every object in JavaScript might have a “prototype”. When we write fred.sayHi() but fred object has no sayHi property, we look for sayHi property on fred’s prototype. If we don’t find it there, we look at the next prototype in the chain — fred’s prototype’s prototype. And so on.

Confusingly, the prototype property of a class or a function does not point to the prototype of that value. I’m not kidding.

function Person() {}

console.log(Person.prototype); // ðŸĪŠ Not Person's prototype
console.log(Person.__proto__); // ðŸ˜ģ Person's prototype

So the “prototype chain” is more like __proto__.__proto__.__proto__ than prototype.prototype.prototype. This took me years to get.

What’s the prototype property on a function or a class, then? It’s the __proto__ given to all objects newed with that class or a function!

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

var fred = new Person('Fred'); // Sets `fred.__proto__` to `Person.prototype`

And that __proto__ chain is how JavaScript looks up properties:

fred.sayHi();
// 1. Does fred have a sayHi property? No.
// 2. Does fred.__proto__ have a sayHi property? Yes. Call it!

fred.toString();
// 1. Does fred have a toString property? No.
// 2. Does fred.__proto__ have a toString property? No.
// 3. Does fred.__proto__.__proto__ have a toString property? Yes. Call it!

In practice, you should almost never need to touch __proto__ from the code directly unless you’re debugging something related to the prototype chain. If you want to make stuff available on fred.__proto__, you’re supposed to put it on Person.prototype. At least that’s how it was originally designed.

The __proto__ property wasn’t even supposed to be exposed by browsers at first because the prototype chain was considered an internal concept. But some browsers added __proto__ and eventually it was begrudgingly standardized (but deprecated in favor of Object.getPrototypeOf()).

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.


This is a long post, eh? I’d say we’re 80% there. Hang on.

We know that when we say obj.foo, JavaScript actually looks for foo in obj, obj.__proto__, obj.__proto__.__proto__, and so on.

With classes, you’re not exposed directly to this mechanism, but extends also works on top of the good old prototype chain. That’s how our React class instance gets access to methods like setState:

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

let c = new Greeting();
console.log(c.__proto__); // Greeting.prototype
console.log(c.__proto__.__proto__); // React.Component.prototype
console.log(c.__proto__.__proto__.__proto__); // Object.prototype

c.render();      // Found on c.__proto__ (Greeting.prototype)
c.setState();    // Found on c.__proto__.__proto__ (React.Component.prototype)
c.toString();    // Found on c.__proto__.__proto__.__proto__ (Object.prototype)

In other words, when you use classes, an instance’s __proto__ chain “mirrors” the class hierarchy:

// `extends` chain
Greeting
  → React.Component
    → Object (implicitly)

// `__proto__` chain
new Greeting()
  → Greeting.prototype
    → React.Component.prototype
      → Object.prototype

2 Chainz.


Since the __proto__ chain mirrors the class hierarchy, we can check whether a Greeting extends React.Component by starting with Greeting.prototype, and then following down its __proto__ chain:

// `__proto__` chain
new Greeting()
  → Greeting.prototype // ðŸ•ĩ️ We start here
    → React.Component.prototype // ✅ Found it!
      → Object.prototype

Conveniently, x instanceof Y does exactly this kind of search. It follows the x.__proto__ chain looking for Y.prototype there.

Normally, it’s used to determine whether something is an instance of a class:

let greeting = new Greeting();

console.log(greeting instanceof Greeting); // true
// greeting (ðŸ•ĩ️‍ We start here)
//   .__proto__ → Greeting.prototype (✅ Found it!)
//     .__proto__ → React.Component.prototype 
//       .__proto__ → Object.prototype

console.log(greeting instanceof React.Component); // true
// greeting (ðŸ•ĩ️‍ We start here)
//   .__proto__ → Greeting.prototype
//     .__proto__ → React.Component.prototype (✅ Found it!)
//       .__proto__ → Object.prototype

console.log(greeting instanceof Object); // true
// greeting (ðŸ•ĩ️‍ We start here)
//   .__proto__ → Greeting.prototype
//     .__proto__ → React.Component.prototype
//       .__proto__ → Object.prototype (✅ Found it!)

console.log(greeting instanceof Banana); // false
// greeting (ðŸ•ĩ️‍ We start here)
//   .__proto__ → Greeting.prototype
//     .__proto__ → React.Component.prototype 
//       .__proto__ → Object.prototype (🙅‍ Did not find it!)

But it would work just as fine to determine if a class extends another class:

console.log(Greeting.prototype instanceof React.Component);
// greeting
//   .__proto__ → Greeting.prototype (ðŸ•ĩ️‍ We start here)
//     .__proto__ → React.Component.prototype (✅ Found it!)
//       .__proto__ → Object.prototype

And that check is how we could determine if something is a React component class or a regular function.


That’s not what React does though. ðŸ˜ģ

One caveat to the instanceof solution is that it doesn’t work when there are multiple copies of React on the page, and the component we’re checking inherits from another React copy’s React.Component. Mixing multiple copies of React in a single project is bad for several reasons but historically we’ve tried to avoid issues when possible. (With Hooks, we might need to force deduplication though.)

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.

So instead, React added a special flag to the base component. React checks for the presence of that flag, and that’s how it knows whether something is a React component class or not.

Originally the flag was on the base React.Component class itself:

// Inside React
class Component {}
Component.isReactClass = {};

// We can check it like this
class Greeting extends Component {}
console.log(Greeting.isReactClass); // ✅ Yes

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.

This is why React moved this flag to React.Component.prototype:

// Inside React
class Component {}
Component.prototype.isReactComponent = {};

// We can check it like this
class Greeting extends Component {}
console.log(Greeting.prototype.isReactComponent); // ✅ Yes

And this is literally all there is to it.

You might be wondering why it’s an object and not just a boolean. It doesn’t matter much in practice but early versions of Jest (before Jest was Good™️) had automocking turned on by default. The generated mocks omitted primitive properties, breaking the check. Thanks, Jest.

The isReactComponent check is used in React to this day.

If you don’t extend React.Component, React won’t find isReactComponent on the prototype, and won’t treat component as a class. Now you know why the most upvoted answer for Cannot call a class as a function error is to add extends React.Component. Finally, a warning was added that warns when prototype.render exists but prototype.isReactComponent doesn’t.


You might say this story is a bit of a bait-and-switch. The actual solution is really simple, but I went on a huge tangent to explain why React ended up with this solution, and what the alternatives were.

In my experience, that’s often the case with library APIs. For an API to be simple to use, you often need to consider the language semantics (possibly, for several languages, including future directions), runtime performance, ergonomics with and without compile-time steps, the state of the ecosystem and packaging solutions, early warnings, and many other things. The end result might not always be the most elegant, but it must be practical.

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

But if you’re also curious... it’s nice to know how it works.

Why Do We Write super(props)?

I heard Hooks are the new hotness. Ironically, I want to start this blog by describing fun facts about class components. How about that!

These gotchas are not important for using React productively. But you might find them amusing if you like to dig deeper into how things work.

Here’s the first one.


I wrote super(props) more times in my life than I’d like to know:

class Checkbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOn: true };
  }
  // ...
}

Of course, the class fields proposal lets us skip the ceremony:

class Checkbox extends React.Component {
  state = { isOn: true };
  // ...
}

A syntax like this was planned when React 0.13 added support for plain classes in 2015. Defining constructor and calling super(props) was always intended to be a temporary solution until class fields provide an ergonomic alternative.

But let’s get back to this example using only ES2015 features:

class Checkbox extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isOn: true };
  }
  // ...
}

Why do we call super? Can we not call it? If we have to call it, what happens if we don’t pass props? Are there any other arguments? Let’s find out.


In JavaScript, super refers to the parent class constructor. (In our example, it points to the React.Component implementation.)

Importantly, you can’t use this in a constructor until after you’ve called the parent constructor. JavaScript won’t let you:

class Checkbox extends React.Component {
  constructor(props) {
    // ðŸ”ī Can’t use `this` yet
    super(props);
    // ✅ Now it’s okay though
    this.state = { isOn: true };
  }
  // ...
}

There’s a good reason for why JavaScript enforces that parent constructor runs before you touch this. Consider a class hierarchy:

class Person {
  constructor(name) {
    this.name = name;
  }
}

class PolitePerson extends Person {
  constructor(name) {
    this.greetColleagues(); // ðŸ”ī This is disallowed, read below why
    super(name);
  }
  greetColleagues() {
    alert('Good morning folks!');
  }
}

Imagine using this before super call was allowed. A month later, we might change greetColleagues to include the person’s name in the message:

  greetColleagues() {
    alert('Good morning folks!');
    alert('My name is ' + this.name + ', nice to meet you!');
  }

But we forgot that this.greetColleagues() is called before the super() call had a chance to set up this.name. So this.name isn’t even defined yet! As you can see, code like this can be very difficult to think about.

To avoid such pitfalls, JavaScript enforces that if you want to use this in a constructor, you have to call super first. Let the parent do its thing! And this limitation applies to React components defined as classes too:

  constructor(props) {
    super(props);
    // ✅ Okay to use `this` now
    this.state = { isOn: true };
  }

This leaves us with another question: why pass props?


You might think that passing props down to super is necessary so that the base React.Component constructor can initialize this.props:

// Inside React
class Component {
  constructor(props) {
    this.props = props;
    // ...
  }
}

And that’s not far from truth — indeed, that’s what it does.

But somehow, even if you call super() without the props argument, you’ll still be able to access this.props in the render and other methods. (If you don’t believe me, try it yourself!)

How does that work? It turns out that React also assigns props on the instance right after calling your constructor:

  // Inside React
  const instance = new YourComponent(props);
  instance.props = props;

So even if you forget to pass props to super(), React would still set them right afterwards. There is a reason for that.

When React added support for classes, it didn’t just add support for ES6 classes alone. The goal was to support as wide range of class abstractions as possible. It was not clear how relatively successful would ClojureScript, CoffeeScript, ES6, Fable, Scala.js, TypeScript, or other solutions be for defining components. So React was intentionally unopinionated about whether calling super() is required — even though ES6 classes are.

So does this mean you can just write super() instead of super(props)?

Probably not because it’s still confusing. Sure, React would later assign this.props after your constructor has run. But this.props would still be undefined between the super call and the end of your constructor:

// Inside React
class Component {
  constructor(props) {
    this.props = props;
    // ...
  }
}

// Inside your code
class Button extends React.Component {
  constructor(props) {
    super(); // 😎 We forgot to pass props
    console.log(props);      // ✅ {}
    console.log(this.props); // 😎 undefined 
  }
  // ...
}

It can be even more challenging to debug if this happens in some method that’s called from the constructor. And that’s why I recommend always passing down super(props), even though it isn’t strictly necessary:

class Button extends React.Component {
  constructor(props) {
    super(props); // ✅ We passed props
    console.log(props);      // ✅ {}
    console.log(this.props); // ✅ {}
  }
  // ...
}

This ensures this.props is set even before the constructor exits.


There’s one last bit that longtime React users might be curious about.

You might have noticed that when you use the Context API in classes (either with the legacy contextTypes or the modern contextType API added in React 16.6), context is passed as a second argument to the constructor.

So why don’t we write super(props, context) instead? We could, but context is used less often so this pitfall just doesn’t come up as much.

With the class fields proposal this whole pitfall mostly disappears anyway. Without an explicit constructor, all arguments are passed down automatically. This is what allows an expression like state = {} to include references to this.props or this.context if necessary.

With Hooks, we don’t even have super or this. But that’s a topic for another day.