Build your own React.js - Part 4. State Updates
Tags: react, javascript, webdev, frontend
Published: 2022-02-21 19:42:24

---
title: Build your own React.js - Part 4. State Updates
published: true
description: We will build a simple version of popular library for building UI - React.js. In this article, we will implement mechanics of state updates.
tags: react, javascript, webdev, frontend
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/xzset8k52mit2jo17ke5.png
---

## Table Of Content
* [Introduction](#introduction)
* [Adding state to class component](#state-component)
* [Update Component](#update-component)

## Introduction <a name="introduction"></a>
In the previous articles, we implemented the mounting process of the class component and its children to the DOM. Although mounting into the DOM is the crucial step of the rendering process in React.js, it's the updating of the DOM where React.js really shines. As you may know, React.js do it by keeping "virtual" DOM in memory and syncing it with the real DOM, thus making DOM manipulations faster.

There are many ways to trigger an update process in React.js. It could be user interaction, some event triggered by setInterval or notification from a web socket. We will use a user interaction because it's the most common.

We know that React.js has `setState` API which updates `state` object and, by default, triggers re-rendering. `setState` can be launched in different parts of the application (except `render()` method of a class component), but for now, we will focus on updating state in response to user interaction with our application. For example, a user clicked a button, which triggered onClick event handler, which in turn updated the local state of the class component by calling `setState`. 

Let's implement this flow but with one restriction, instead of adding support for event handlers to DOM nodes, e.g. onClick attribute of a button, we will use the `click` event listener and update the local state of a class component each time user clicks somewhere in the `window` of a browser. The reason for this restriction is that supporting event handling in React.js is a topic for another conversation. Maybe we will return to this subject later.

## Adding state to class component <a name="state-component"></a>
For now, let's change the App class component for our future local state implementation.

We will start by adding the `constructor` method to the App class component. Inside the constructor, we first call `super` method. This is an important step because overwise the state initialization won't work. If you want to know more about `super` [Dan Abramov](https://twitter.com/dan_abramov) wrote [a whole article about it](https://overreacted.io/why-do-we-write-super-props/).
Secondly, we initialize `clickCount` field with the value `0` inside `state` object of the App class component. We will also change the content of the `render` method with `this.state.clickCount` value rendering inside `div` element.

```js
  // index.js

 class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      clickCount: 0,
    }
  }

  render() {
    return {
      type: "div",
      props: {
        children: this.state.clickCount
      }
    };
  }
}
```

>Here I want to go back in time a little bit for those who follow `Build your own React.js` series from the beginning. Those who are new can skip this part. 

Since we now render value with the type of `number` in the `div` element, we need to teach our `DOMComponentWrapper` to render numbers. We will do it by adding `typeof props.children === "number"` in the condition. 
```js
   // DOMComponentWrapper.js
  _createInitialDOMChildren(props) {
    if (
      typeof props.children === "string" || 
      typeof props.children === "number"
    ) {
      this._domNode.textContent = props.children;
    }
  }
```
>Ok, let's return back to present times. Sorry for that little  adventure in time.

Now we need to call `setState` every time a user clicks the left button of the mouse. For that, we need to add an event listener (remember we agreed that we won't add support for event handling?). Usually, we add an event listener in `componentDidMount` component's lifecycle, but since we don't have lifecycles yet, we are going to add it in the `constructor` of a class component. 

>I skipped clearing an event listener on the class component unmount intentionally (don't do it in a real project!).

```js
  // index.js

 class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      clickCount: 0,
    }
    window.addEventListener('click', () => {
      this.setState({clickCount: this.state.clickCount + 1});
    })
  }
 ...

```

Let's now add `setState` method to the `Component` class so that the App class component can inherit it.

```js
class Component {
  constructor() {
    ...
    this._pendingState = null;
    ...
  }
  setState(partialState) {
    this._pendingState = partialState;
    UpdateQueue.enqueueSetState(this, partialState);
  }
  ...
```
Method `setState` takes `partialState` as an argument. It's called `partialState` because setState doesn't require you to provide a full updated state object as an argument, it only needs part of the state that you want to update, so it can merge it into the current `state` object.

We assign `partialState` to `this._pendingState` in the constructor and then call `UpdateQueue.enqueueSetState(this, partialState)` with an instance of the App class component and `partialState` as an arguments.

Let's create `UpdateQueue.js` with `enqueueSetState` function.

```js
// UpdateQueue.js
import Reconciler from "./Reconciler";

function enqueueSetState(instance, partialState) {
  instance._pendingState = Object.assign(
    {}, 
    instance.state, 
    partialState
  );
  Reconciler.performUpdateIfNecessary(instance);
}

```
Nothing special here, we just take `partialState` and merge it with the `state` object of the instance using `Object.assign`. Empty object as a first argument is just making sure that we create a new object every time.

In the real React.js library `enqueueSetState` also queueing multiple `partialStates` so that at the right time it could do [batch update](https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous).

After that, we pass control to `Reconciler.performUpdateIfNecessary(instance)` which in turn passes control back to the method `performUpdateIfNecessary` of the instance of the App class component which in turn inherited from `Component` class.

```js
// Reconciler.js

function performUpdateIfNecessary(component) {
  component.performUpdateIfNecessary();
}
```

>It might seem weird to you that we call this intermediate `performUpdateIfNecessary` function that does nothing and then just call Component's `performUpdateIfNecessary` from it. Remember, in the real React.js there is more complicated logic inside these functions (such as batch state update) we just skip this logic and try to preserve the order of function calls.

In the `Component` class, we create `performUpdateIfNecessary` method and call `Component`'s `updateComponent` method from it. 
```js
// Component.js

performUpdateIfNecessary() {
    this.updateComponent(this._currentElement);
}
```

## Update Component <a name="update-component"></a>
Now, let's look at the `updateComponent` method. It's a big one, so let's go through it step by step.

```js
  updateComponent(nextElement) {
    this._currentElement = nextElement; // 1
    this.props = nextElement.props;
    this.state = this._pendingState; // 2
    this._pendingState = null;

    let prevRenderedElement = this._renderedComponent._currentElement;
    let nextRenderedElement = this.render(); // 3

    if (shouldUpdateComponent(prevRenderedElement, nextRenderedElement)) { // 4
      Reconciler.receiveComponent(this._renderedComponent, nextRenderedElement);
    }
  }
...
```
1. First, we update `_currentElement` and `props` of the App class component instance to the `nextElement` values.
    ```js
    this._currentElement = nextElement; 
    this.props = nextElement.props;
    ```
    In our case the `nextElement` will be just object:
    ```js
    {
      props: {
        title: "React.js"
      },
      type: App
    }
    ``` 


2. Then we assign `_pendingState` which is `{ clickCount: 1 }` to the current `state` of the App class component instance. And we clear `_pendingState` after that by setting it to `null`.
    ```js
    this.state = this._pendingState;
    this._pendingState = null;
    ```

3. We assign `this._renderedComponent._currentElement` to `prevRenderedElement` variable and `this.render()` to `nextRenderedElement` variable.
    ```js
    let prevRenderedElement = this._renderedComponent._currentElement;
    let nextRenderedElement = this.render();
    ```
    The values of these variables, in our case, are following:
    ```js
    // prevRenderedElement 
    {
        "type": "div",
        "props": {
            "children": 0 // this.state.clickCount
        }
    }
    // nextRenderedElement 
    {
        "type": "div",
        "props": {
            "children": 1 // this.state.clickCount
        }
    }
    ```
    As you can see it's just the state of the `div` element in the App class component's `render` method before and after the user clicked and the event listener called `this.setState({clickCount: this.state.clickCount + 1})` in the constructor of the App class component.

4. With these preparations, we are ready to decide whether should we update the component or just re-mount it. We call `shouldUpdateComponent` with the previous and the next `div` element. 
    ```js
    shouldUpdateComponent(prevRenderedElement, nextRenderedElement)
    ```

    Let's create a file with the name `shouldUpdateComponent.js` and create `shouldUpdateComponent` function inside:

    ```js
    // shouldUpdateComponent.js

    function shouldUpdateComponent(prevElement, nextElement) {
      // this needs only for primitives (strings, numbers, ...)
      let prevType = typeof prevElement;
      let nextType = typeof nextElement;

      if (prevType === 'string') {
        return nextType === 'string';
      }

      return prevElement.type === nextElement.type;
    }

    ```
    Here you can see one of the [two assumptions](https://reactjs.org/docs/reconciliation.html#motivation) that React.js makes when comparing two trees of elements.

    >Two elements of different types will produce different trees.

    In our case, the element `div` doesn't change its type so we can reuse the instance and just update it.

5. Let's return to `updateComponent` method of the Component class.
    ```js
    if (
         shouldUpdateComponent(
           prevRenderedElement, 
           nextRenderedElement
         )
        ) {
          Reconciler.receiveComponent(
            this._renderedComponent, 
            nextRenderedElement
          );
        }
        ...
     ```
    We know that, in our case, `shouldUpdateComponent` will return `true` and `Reconciler.receiveComponent` will get called with the following parameters:

    ```js
    // this._renderedComponent
    DOMComponentWrapper {
        _currentElement: {
            type: "div",
            props: {
                "children": "0"
            }
         },
        _domNode: {}
     }

    // nextRenderedElement
    {
        type: "div",
        props: {
            children: 1
        }
    }
    ```
6. Let's add `receiveComponent` to the `Reconciler`.

    ```js
    // Reconciler.js

    function receiveComponent(component, element) {
      component.receiveComponent(element);
    }
    ```
    Again, this is the place where more optimizations happen in the real React.js, for now, we won't focus on that.

    The important part here is that the `component` argument of the function is not the `App` class component, but `DOMComponentWrapper`. That's because DOM elements (div, span, etc) that need to be rendered are wrapped in `DOMComponentWrapper` so that handling of these elements state (props, children) was easier and similar to handling class components state ([see previous posts about DOMComponentWrapper](https://dev.to/rinatrezyapov/build-your-own-react-js-part-2-react-component-350h#implementing-domcomponentwrapper)). 

7. Now we need to go to `DOMComponentWrapper` and add `receiveComponent` method.
    ```js
      receiveComponent(nextElement) {
        this.updateComponent(this._currentElement, nextElement);
      }

      updateComponent(prevElement, nextElement) {
        this._currentElement = nextElement;
        // this._updateDOMProperties(prevElement.props, nextElement.props);
        this._updateDOMChildren(prevElement.props, nextElement.props);
      }
     ```
     As you can see `updateComponent` for `DOMComponentWrapper` looks a bit different from `Component`'s. I intentionally commented out `this._updateDOMProperties` because we are not interested in updating DOM properties for now and it will only complicate things.

8. So let's jump into `this._updateDOMChildren`:

    ```js
      _updateDOMChildren(prevProps, nextProps) {
        let prevType = typeof prevProps.children;
        let nextType = typeof nextProps.children;
        if (prevType !== nextType) {
          throw new Error('switching between different children is not supported');
         }

        // Childless node, skip
        if (nextType === 'undefined') {
          return;
        }

         if (nextType === 'string' || nextType === 'number') {
          this._domNode.textContent = nextProps.children;
         }
       }
     ```
    First, we throw an error if, in our case, the type of children of our `div` element is changing `prevType !== nextType`. For example from number `0` to string `no data`. We won't support it for now.

    Secondly, we check if `div` element has children at all `nextType === 'undefined'`. If not, we skip.

    Then we check if the type of `children` of the `div` element is string or number. That's our case because `this.state.clickCount` (which is child of the `div`) has the type of `number`.

     So we just grab the `nextProps.children` and insert it into `div` text content.

Let's stop here because we already covered too much. At this point, you'll be able to open our app and see the number incrementing at each click. That means our custom written React.js library can handle the state. 

Congratulation!

In the next posts, we will continue to improve the state handling in our library.

Links:
1. <a href="https://github.com/RinatRezyapov/build-own-reactjs/commit/11ae9ae21996894e1d1b5260870d738ece80028e" target="_blank" rel="noopener">Github repo with the source code from this article</a>
2. <a href="https://codesandbox.io/s/build-your-own-react-js-part-4-state-updates-9i1ojb" target="_blank" rel="noopener">Codesandbox with the code from this article (refresh Codesandbox page if you don't see results)</a>
3. <a href="https://www.youtube.com/watch?v=_MAD4Oly9yg" target="_blank" rel="noopener">Building React From Scratch talk</a>
4. <a href="https://reactjs.org/docs/implementation-notes.html" target="_blank" rel="noopener">React.js docs regarding Building React From Scratch talk</a>