Build Your Own React

Written by ofirdagan | Published 2017/05/19
Tech Story Tags: javascript | reactjs | react | how-to | front-end-development

TLDRvia the TL;DR App

Abstract- In this post I’ll create a full working version of react step by step. It won’t be an efficient version but it give you a glimpse to how react works under the hood. If you’re familiar with react / web / dom etc.. you can go ahead and skip to step 1. Alternatively if you’re the video type, you can check out my talk on youtube.

Intro

Before writing react apps, like many others, I wrote in angular. The thing about angular is that if you want to be a good angular developer you need to know how angular works. The more you know, the better you’ll be. With react though, it doesn’t seems to be the case. You learn about class components and stateless components. You learn about props and state and that you should never change the props. Then you learn that in case you want something to be dynamic you call setState and that’s pretty much it. Excluding lifecycle methods and other small features there is not a lot more you need in order to write good react apps. There’s no doubt that the learning curve of react vs angular is much smaller.

But, I became a programmer because I like knowing how stuff works. What makes things tick. It doesn’t matter if it’s the international space station, tesla car or a vending machine. When I see something that interests me, soon then after I start questioning how it works. The same happened with react. I saw a great talk about react under the hood and I thought to myself.. That’s doesn’t look so complicated.. I can try it out.

What is react anyway?

Before we’ll dive in writing react we should know what is it that we want to build. As I see it, react is a library for building dynamic trees. In the web use case react will build a DOM tree. However it doesn’t have to be DOM. With react native, react builds a native app hierarchy of ui controllers. You could even write arduino apps in react.

What is a DOM tree anyway?

DOM stands for document object model. Every html document is built from elements that are called “dom elements”. The _<html>_ is a dom element. Also _<div>_ and _<h1>_ etc. The tree has a root, usually the html tag. Every node in that tree has its own properties and it may have children which are also dom elements. For example. In the following image you can see a part of an html document and its representation as a tree of objects.

A few words about react and jsx

If you’re already familiar with react you probably use jsx. So a simple component can look like this:

const mySimpleComponent = () => {return <h1>Hello World</h1>;}

After running babel or your other favorite transpiler this component will look like this:

const mySimpleComponent = () => {return React.createElement(‘h1’, null, `Hello World`);}

A special babel plugin will transpile the jsx call to the underlying js api that react provides. In our case the <h1> tag got transpiled into a React.createElement call where the first argument is the tag name, the second argument is the properties and the third is its children.

Let’s look at a bit more complicated jsx example:

<div style={{color: yellow}}><Title text=`I'm a title`/><Person name=`moki`/></div>

Which will result in:

React.createElement(`div`, {style: {color: yellow}},React.createElement(Title, {text: `I'm a title`}, null),React.createElement(Person, {name: `moki`}, null));

Now that we’re in sync we can start building our own react.

Step 1- DOM elements (Hello World)

First, I’ll create a host html document for my app. It will load my react.js version, app.js for the app’s logic and it will have a div with id root to attach my react app to.

The way we’re going to build react will be in what I like to call ADD. It’s like TDD but instead of Test Driven Development it’s stands for Application Driven Development. In each step I’ll show the app.js that I would like to render. Then I’ll implement it in the react.js file.

My firstapp.js looks like this:

In order for this code to work I will need to implement React.createElement and ReactDOM.render

*ReactDOM is a separate module from react but for the sake of simplicity I’ll write them both together.

Let’s do this. Here’s myreact.js :

And we got an hello world working! You can see the result here.

Step 2 — Render non DOM elements or as we like to call them in react `components`

2.1- We want to add support for stateless components

app.js :

The only thing changed here is that now the element can be a function. In case it is, we’ll invoke it.

react.js :

*result

2.2- Not all children are born equally. We want to add support for non plain text children. Let’s refine the way we handle children.

app.js :

react.js :

*result

2.3- Add support for class components

app.js :

As we did with the stateless components. We will find out if it’s a class and in case it is, we’ll create a new class instance and call its render function.

react.js:

*result

2.4- It’s time for some good old refactor. Our anElement function is getting too long. Let’s extract some methods out. Also, let’s create a react-utils.js for miscellaneous functions such as isClass, isStatelessComponent etc..

Step 3- Props and State

To recap, up until now we rendered dom elements, stateless components and class components. Let’s add props and state to the mix

3.1- Stateless component props

app.js :

This one is easy. We just need to pass in the props to the component (function)

react.js :

*result

3.2- Class component props

app.js :

You should notice that the Hello class now extends React.Component. I did this so I will have a common parent class for all of my react classes for purposes such as assigning the props on the class instance.

Component class:

Now that we have the Component class, we’ll pass the props in the constructor and we’re done.

3.3- Attributes

This simple component should show an alert on button click.

app.js :

As with the children, we’ll iterate over the attributes and set event listeners to attributes that starts with on* and set attribute for the rest.

react.js :

*result

3.4- Refactor. handleHtmlElement became too big. We’ll extract out appendChild and appendProp functions. Also we can clean the code a little bit using lodash.

3.5- State

We’re ready to write a real react app. Let’s write a counter app. It will have two buttons to increment and decrement the counter and will present the current value

app.js :

To make this work we need to implement the setState function. My naive algorithm will delete the entire dom on every setState call and will render it again. For that I will need to save a reference to the root react element and to the root dom element.

react.js

Next I would like to save the classes that I’ve created so when reRender happens I won’t create them again and lose my state. To achieve this I will do the following:

  • Hold a cache of the instantiated classes
  • When handling a class. In case of a match, return the cached instance
  • Instead of calling render straight away return an object with a special flag that marks it as a react class.
  • When appending a child node. If it’s a react class call its render

handleClass :

handleChild :

*result

That’s it. We have our own working react. Don’t believe me? Check out this todo app that I took from the web. I didn’t changed anything except for the references to the real react and react-dom.

The todo app doesn’t impress you much? How about this minesweeper game?

Now what?

Let’s reflect on what I’ve showed you a little bit. The react version I wrote… It doesn’t seem super efficient. On every call to setState I clear the entire dom and creates it from scratch. Also the first thing you hear when someone talks about how react works is the mysterious virtual dom. I didn’t write anything virtual in my code nor did I mention it.

Time to face the truth

Time for some performance analysis. I timed the time for first render of the minesweeper game both with my react and with the real react. The results are going to surprise you.

It seems that my react is doing about 2.5 times better than the real react. But let’s hold our horses for a minute. If we continue to play we can see the time it takes for every setState to finish. With my react it takes between 2–8 ms as opposed to 0.01ms for the real react. This difference is why we love react and use it so often. Besides being super intuitive and easy to learn, it’s very fast and keep getting faster.

The react algorithm

First my algorithm: on every re-render I

  • Clear the dom
  • Render the DOM from scratch

If we look at the code we will see that everything we wrote was javascript. We didn’t use any html or other languages. So what if, instead of creating the dom elements straight away we will keep a tree model of the dom made by javascript objects. It’s very easy to represent our DOM as a javascript object right? We saw that in the beginning.

So a better algorithm will be:

  • Call render on the js tree model
  • Read the current dom
  • Figure out the changes
  • Apply only these changes on the dom

This will make things faster. But we’re smarter than that. We want to keep our reads and writes to the dom to as minimal as we can. We already have a js tree model (let’s call it virtual dom from now) that reflects the way our dom should look like.

So we can have an even better algorithm. On re-render, the idea here is to have two virtual doms one always represents the current dom and the other will represent the future dom. On reRender:

  • Create a new virtual dom
  • Figure out the difference between it and the current virtual dom (diffing between two js tress can be done pretty fast)
  • Apply only the changes on the real dom

The process of diffing and merging the diffs into the real dom tree is called reconciliation. And this is how react really works.

Fiber

You might have heard about fiber. Fiber is the new reconciliation algorithm the guys at react are working on. It should be out soon. The idea behind fiber is simple. Up until now the reconciliation process was linear. Once it started you couldn’t stop it. Fiber aims to change that. The motivation for it is priorities. We realize there are some changes that are more important than others. For example, if you have a text node that needs to be change and a heavy animation to draw. It’s important that you will draw the animation first otherwise the user will notice the ui lagging. If the text node will change in a few ms delay the user won’t notice it. Fiber has a built in interrupt mechanism. Think of it as a pause button. At any given moment during the reconciliation. React can tell fiber to pause, do other calculations such as animations and then continue. This will cause our apps to look much more fluent.

The beauty of it all is that it’s totally transparent to the developer. The reconciliation algorithm is an implementation detail. We saw that with my own lousy implementation of it. The apps worked but not very well.. The simplicity of react’s api and the complexity of its implementation is what makes it such a powerful tool.

All the code you’ve seen on this post is available here. Catch me on twitter at @ofirdagan2 if you have any questions or just to say hi :)


Published by HackerNoon on 2017/05/19