Reactでhello worldするまでを追ってみた

Reactでhello worldされるまでのコードを追ってみました。
React.createElementとReactDOM.renderの流れを追いたいと思います。
間違っている箇所などコメントいただけるととても喜びます。


## 前提

- 以下のような箇所は条件分岐されているような箇所は飛ばします。
  - `__DEV__`や`enableSchedulerTracing`変数で条件分岐されている
- コメントアウトは削除します。
- 読みやすさのためにコードを省力している部分は、以下のようなコメントアウトをします。

```js

// --- 省略した処理の説明 --- 
```

### 使用したもの

- chromeのdevtoolsで確認しやすいようにsetTimeoutが挟まれてます。


```jsx:index.dev.jsx
import React from 'react';
import ReactDOM from 'react-dom';

function App (props) {
  return (
    <div>
      Hello world
    </div>
  );
}

window.addEventListener('load', function () {
  setTimeout(function () {
    ReactDOM.render(
      <App/>,
      document.querySelector('#app-root')
    );
  }, 2000);
});
```

```json:package.json
{
  "name": "experiment",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "webpack-dev-server --progress --color --config ./webpack.config.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "react": "^16.8.1",
    "react-dom": "^16.8.1",
    "webpack": "^4.29.3",
    "webpack-cli": "^3.2.3",
    "webpack-dev-server": "^3.1.14"
  }
}
```


### 評価されるコード

- babelを通したコードです。
- 以下の順番で実行をみていきます。
    - 1. React.createElement(App, null)
    - 2. ReactDOM.render(\<App/\>, document.querySelector('#app-root')

```jsx
"use strict";

var _react = _interopRequireDefault(require("react"));

var _reactDom = _interopRequireDefault(require("react-dom"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function App(props) {
  return _react.default.createElement("div", null, "Hello world");
}

window.addEventListener('load', function () {
  setTimeout(function () {
    _reactDom.default.render( // <= 2
      _react.default.createElement(App, null), // <= 1
      document.querySelector('#app-root')
    );
  }, 2000);
});
```


## 1. React.createElement(App, null)

### process

```
- createElement
    - return ReactElement
```

### createElement

- まずは`React.createElement`実行していきます。
- `createElement`は引数を元にごにょごにょしてパラメータを整えて、Elementの生成処理を`ReactElement`関数に任せ、その返り値をそのまま返します。
- 今回渡す引数にはpropsもchildrenもないので、ReactElementに渡す引数は非常に寂しいものになりました。
- `ReactCurrentOwner.current`という奴が出てきましたが、こちらはFiberのあたりを読んでみないとよくわからなそうです。

https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react/src/ReactElement.js#L171

```jsx

// type = App
// config = null
// children = undefined
function createElement (type, config, children) {

  // --- arrange parameter for ReactElement ---

  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );

}
```

### ReactElement

- 続いて`ReactElement`の呼び出しです。こちらは基本的に受け取ったデータをオブジェクトに詰めることと、`$$typeof`というスペシャルなプロパティを定義するだけです。

https://github.com/facebook/react/blob/3ae94e1885b673543a30a05906c4f9a0e4b682cb/packages/react/src/ReactElement.js#L111

```js

// type,                     = App
// key,                      = null
// ref,                      = null
// self,                     = null
// source,                   = null
// ReactCurrentOwner.current = null
// props,                    = {}
const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    $$typeof: REACT_ELEMENT_TYPE,
    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner,
  };

  // --- dev: awesome codes ---

  return element;
};
```

- そして以下がcreateElementの今回の返り値になります。以降`root-ReactElement`と呼びます。
- こちらは次の`ReactDOM.render`の第一引数にそのまま渡されます。

```js

// root-ReactElement
{
  $$typeof: Symbol(react.element),
  type: App,
  key: null,
  ref: null,
  props: {},
  _owner: null,
}
```


## 2. ReactDOM.render(\<App/\>, document.querySelector('#app-root'))

- 次にmountをするために`ReactDOM.render`関数を実行していきます。
- こちらは結構処理が多いので、小分けにしていきたいと思います。

### process

```

- ReactDOM.render
    - legacyRenderSubtreeIntoContainer
        - legacyCreateRootFromDOMContainer     <= 2-1
          - ReactRoot
        - ReactRoot.prototype.render           <= 2-2
            - updateContainer
              - scheduleRootUpdate
                - enqueueUpdate                <= 2-2-1
                - scheduleWork                 <= 2-2-2
                  ...
                  - performWork                <= 2-2-2-1
                      ...
                      - renderRoot             <= 2-2-2-1-1
                        ...
                        - performUnitOfWork    <= 2-2-2-1-1-1
                          ...
                          - beginWork          <= 2-2-2-1-1-2
                          - completeUnitOfWork <= 2-2-2-1-1-3
                      - completeRoot           <= 2-2-2-1-2
```

### ReactDOM.render

- まずはrender関数の実行です。第一引数には前節で生成されたReactElementの`root-ReactElement`が渡されています。
- 第３引数のcallbackは渡していないのでundefinedです。


https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-dom/src/client/ReactDOM.js#L673

```js

  render(
    element: React$Element<any>, // root-ReactElement
    container: DOMContainer,     // div#app-root
    callback: ?Function,         // undefined
  ) {

    // --- dev: warning ---

    return legacyRenderSubtreeIntoContainer(
      null,
      element,
      container,
      false,
      callback,
    );
  },
```

### legacyRenderSubtreeIntoContainer

- 次にlegacyRenderSubtreeIntoContainerです。
- 初回のrenderのため、`legacyCreateRootFromDOMContainer`という関数を呼び出し、`ReactRoot`インスタンスを生成し、インスタンスの`legacy_renderSubtreeIntoContainer`メソッドを呼び出し、mountの処理をさらに進めていきます。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-dom/src/client/ReactDOM.js#L540

```js

function legacyRenderSubtreeIntoContainer(
  parentComponent: ?React$Component<any, any>, // null
  children: ReactNodeList,                     // root-ReactElement
  container: DOMContainer,                     // div#app-root
  forceHydrate: boolean,                       // false
  callback: ?Function,                         // undefined
) {
  // --- dev: warning ---

  let root: Root = (container._reactRootContainer: any);
  if (!root) {
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer( // <= 2-1
      container,
      forceHydrate,
    );

    // --- arrange callback ---

    unbatchedUpdates(() => {
      if (parentComponent != null) {
        // --- process when parentComponent is not null ---
      } else {
        root.render(children, callback);                                     // <= 2-2
      }
    });
  } else {

    // --- process when render is not initial ---

  }
  return getPublicRootInstance(root._internalRoot);
}
```

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-dom/src/client/ReactDOM.js#L495

### 2-1 legacyCreateRootFromDOMContainer

- 渡されたDOMのchildrenの掃除と`ReactRoot`の生成を担います。
- 生成物をそのまま返します。

```js

function legacyCreateRootFromDOMContainer(
  container: DOMContainer,
  forceHydrate: boolean,
): Root {
  const shouldHydrate =
    forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
  if (!shouldHydrate) {
    let warned = false;
    let rootSibling;
    while ((rootSibling = container.lastChild)) {

      // --- dev: warn on SSR related ---

      container.removeChild(rootSibling);
    }
  }

  // --- dev: warn on hydrate ---

  const isConcurrent = false;
  return new ReactRoot(container, isConcurrent, shouldHydrate);
}
```

#### 2-1-1 ReactRoot

- ReactRootというものを生成するために、`react-reconciler`の方へ飛んだりします。
- `_internalRoot`プロパティには`FiberRoot`なる全ての`Fiber`の親っぽいものが突っ込まれます。
- こちらは`current`のプロパティに`HostRoot`のtagをもつ`Fiber`を持ちます。
  - こちらの`Fiber`は以降`root-HostRootFiber`と呼びます。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-dom/src/client/ReactDOM.js#L365

```js

function ReactRoot(
  container: DOMContainer, // 
  isConcurrent: boolean,
  hydrate: boolean,
) {
  const root = createContainer(container, isConcurrent, hydrate);
  this._internalRoot = root;
}


// react-reconciler from here

export function createContainer(
  containerInfo: Container,
  isConcurrent: boolean,
  hydrate: boolean,
): OpaqueRoot {
  return createFiberRoot(containerInfo, isConcurrent, hydrate);
}

export function createFiberRoot(
  containerInfo: any,
  isConcurrent: boolean,
  hydrate: boolean,
): FiberRoot {
  const uninitializedFiber = createHostRootFiber(isConcurrent);

  let root;
  if (enableSchedulerTracing) {
    // --- root with scheduler tracing ---
  } else {
    root = ({
      current: uninitializedFiber,
      containerInfo: containerInfo,
      pendingChildren: null,

      pingCache: null,

      earliestPendingTime: NoWork,
      latestPendingTime: NoWork,
      earliestSuspendedTime: NoWork,
      latestSuspendedTime: NoWork,
      latestPingedTime: NoWork,

      didError: false,

      pendingCommitExpirationTime: NoWork,
      finishedWork: null,
      timeoutHandle: noTimeout,
      context: null,
      pendingContext: null,
      hydrate,
      nextExpirationTimeToWorkOn: NoWork,
      expirationTime: NoWork,
      firstBatch: null,
      nextScheduledRoot: null,
    }: BaseFiberRootProperties);
  }

  uninitializedFiber.stateNode = root;

  return ((root: any): FiberRoot);
}
```

```js

// root-HostRootFiber
FiberNode {
  actualDuration: 0
  actualStartTime: -1
  alternate: null
  child: null
  childExpirationTime: 0
  contextDependencies: null
  effectTag: 0
  elementType: null
  expirationTime: 0
  firstEffect: null
  index: 0
  key: null
  lastEffect: null
  memoizedProps: null
  memoizedState: null
  mode: 4
  nextEffect: null
  pendingProps: null
  ref: null
  return: null
  selfBaseDuration: 0
  sibling: null
  stateNode: root,
  tag: 3
  treeBaseDuration: 0
  type: null
  updateQueue: null
  _debugID: 1
  _debugIsCurrentlyTiming: false
  _debugOwner: null
  _debugSource: null
}
```

- ここまできて、前述の`legacyRenderSubtreeIntoContainer`内の`root`の部分は以下となります。
- 以降こちらのインスタンスを`root-ReactRoot`、中身の`_internalRoot`を`root-internalRoot`と省略します。

```js

// root-ReactRoot
ReactRoot {
  // root-internalRoot
  _internalRoot: {
    containerInfo: div#app-root,
    context: null,
    current: root-HostRootFiber,
    didError: false,
    earliestPendingTime: 0,
    earliestSuspendedTime: 0,
    expirationTime: 0,
    finishedWork: null,
    firstBatch: null,
    hydrate: false,
    interactionThreadID: 1,
    latestPendingTime: 0,
    latestPingedTime: 0,
    latestSuspendedTime: 0,
    memoizedInteractions: Set(0) {},
    nextExpirationTimeToWorkOn: 0,
    nextScheduledRoot: null,
    pendingChildren: null,
    pendingCommitExpirationTime: 0,
    pendingContext: null,
    pendingInteractionMap: Map(0) {},
    pingCache: null,
    timeoutHandle: -1,
  }
}
```

### 2-2 ReactRoot.prototype.render

- 前節までで生成された`root-ReactRoot`の`render`メソッドを呼び出して、いよいよReactをmountしていきます。
- `unbatchedUpdates`という関数が出てきていますが、、今回のケースでは渡された関数をそのまま実行するだけです。
- `ReactWork`のインスタンスも生成して、その`_onCommit`メソッドを`updateContainer`に渡しています。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-dom/src/client/ReactDOM.js#L373

```js

ReactRoot.prototype.render = function(
  children: ReactNodeList, // root-ReactElement
  callback: ?() => mixed,  // undefined
): Work {
  const root = this._internalRoot;
  const work = new ReactWork();
  callback = callback === undefined ? null : callback;
  // --- dev: warning ---
  // --- set callback ---
  updateContainer(children, root, null, work._onCommit);
  return work;
};
```

- `updateContainer`を実行します
- `computeExpirationForFiber`関数を実行して、`expirationTime`という処理が終わっているべき時間を求めています
  - こちらは以降`root-expirationTime`と呼びます
- `scheduleRootUpdate`を実行して更新処理を進めます。


https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberReconciler.js#L283
https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberReconciler.js#L162

```js

export function updateContainer(
  element: ReactNodeList,                      // root-ReactElement
  container: OpaqueRoot,                       // root-internalRoot
  parentComponent: ?React$Component<any, any>, // null
  callback: ?Function,                         // work._onCommit
): ExpirationTime {
  const current = container.current;
  const currentTime = requestCurrentTime();
  const expirationTime = computeExpirationForFiber(currentTime, current);
  return updateContainerAtExpirationTime(
    element,
    container,
    parentComponent,
    expirationTime,
    callback,
  );
}

export function updateContainerAtExpirationTime(
  element: ReactNodeList,                      // root-ReactElement
  container: OpaqueRoot,                       // root-internalRoot
  parentComponent: ?React$Component<any, any>, // null
  expirationTime: ExpirationTime,              // root-expirationTime
  callback: ?Function,                         // work._onCommit
) {
  const current = container.current;

  // --- dev: warning ---

  const context = getContextForSubtree(parentComponent);
  if (container.context === null) {
    container.context = context;
  } else {
    container.pendingContext = context;
  }

  return scheduleRootUpdate(current, element, expirationTime, callback);
}
```

```js

// root-expirationTime
1073741823
```


- `scheduleRootUpdate`を実行します。
- やっていることは
  - `update`を作成。
  - `flushPassiveEffects`はなんかpassiveなeffectをflushするんでしょう
  - `enqueueUpdate`で`current`のFiberに`update`を盛る
  - `scheduleWork`で`Fiber`の`update`を実行する
  - と思われます


https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberReconciler.js#L115

```js

function scheduleRootUpdate(
  current: Fiber,                 // root-HostRootFiber
  element: ReactNodeList,         // root-ReactElement
  expirationTime: ExpirationTime, // root-expirationTime
  callback: ?Function,            // work._onCommit
) {
  // --- dev: warning ---

  const update = createUpdate(expirationTime);
  update.payload = {element};

  // --- arrange callback ---

  flushPassiveEffects();
  enqueueUpdate(current, update);        <= 2-2-1
  scheduleWork(current, expirationTime); <= 2-2-2

  return expirationTime;
}
```

- 途中で出てくる`update`はこんな感じです。以降`root-update`と呼びます。
- こちらを保持する`updateQueue`は連結リストであるので、`next`のプロパティがありますね。

```js

// root-update
{
  "expirationTime": root-expirationTime,
  "tag": 0,
  "payload": root-ReactElement,
  "callback": null,
  "next": null,
  "nextEffect": null
}
```

#### 2-2-1 enqueueUpdate

- `enqueueUpdate`を呼び出して、`fiber`の`updateQueue`に先ほど生成した`update`を`appendUpdateToQueue`によって盛り盛りします。
- `root-HostRootFiber`は生成されたばかりでしたので、`updateQueue`はここで`createUpdateQueue`によって作られます。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactUpdateQueue.js#L220

```js

// fiber  = root-HostRootFiber
// update = root-update
export function enqueueUpdate<State>(fiber: Fiber, update: Update<State>) {
  const alternate = fiber.alternate;
  let queue1;
  let queue2;
  if (alternate === null) {
    queue1 = fiber.updateQueue;
    queue2 = null;
    if (queue1 === null) {
      queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
    }
  } else {
    // --- process when there are two fiber nodes ---
  }
  if (queue2 === null || queue1 === queue2) {
    appendUpdateToQueue(queue1, update);
  } else {
    // --- process when there are two updateQueues ---
  }

  // --- dev: warning ---
}
```

```js

// root-HostRootFiber.updateQueue
{
  "baseState": null,
  "firstUpdate": root-update,
  "lastUpdate": root-update,
  "firstCapturedUpdate": null,
  "lastCapturedUpdate": null,
  "firstEffect": null,
  "lastEffect": null,
  "firstCapturedEffect": null,
  "lastCapturedEffect": null
}
```

#### 2-2-2 scheduleWork

- `scheduleRootUpdate`の実行に戻りまして、次は`scheduleWork`を実行します。ここからが長いです。。。
- `scheduleWorkToRoot`を実行して、`root`をgetします。
- 見つかったrootとそのrootがもつ`expirationTime`を`requestWork`に渡します。
- if文で出てくる`nextRoot`はこのファイルが持つグローバル変数です。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberScheduler.js#L1849

```js

// fiber          = root-HostRootFiber
// expirationTime = root-expirationTime
function scheduleWork(fiber: Fiber, expirationTime: ExpirationTime) {
  const root = scheduleWorkToRoot(fiber, expirationTime);
  if (root === null) {
    // --- dev: warning ---
    return;
  }

  // --- process when interruption occuered

  markPendingPriorityLevel(root, expirationTime);
  if (
    !isWorking ||
    isCommitting ||
    nextRoot !== root
  ) {
    const rootExpirationTime = root.expirationTime;
    requestWork(root, rootExpirationTime);
  }
  // --- dev: warning ---
}
```

- `scheduleWorkToRoot`の実行です
- 引数の`expirationTime`の方が`root-HostRootFiber`のもつ`expirationTime`よりも大きいので、この値がセットされます。
- `root-HostRootFiber`の`return`プロパティが`null`かつ`tag`に`HostRoot`の値を持っていますので、この`fiber`のもつ`stateNode`(`root-internalRoot`)を`FiberRoot`ということにしてこの関数の実行を終了します。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberScheduler.js#L1744

```js

// fiber          = root-HostRootFiber
// expirationTime = root-expirationTime
function scheduleWorkToRoot(fiber: Fiber, expirationTime): FiberRoot | null {
  recordScheduleUpdate();

  // --- dev: warning ---

  // Update the source fiber's expiration time
  if (fiber.expirationTime < expirationTime) {
    fiber.expirationTime = expirationTime;
  }
  // --- process when there is alternate ---
  let node = fiber.return;
  let root = null;
  if (node === null && fiber.tag === HostRoot) {
    root = fiber.stateNode;
  } else {
    // --- process when its not root ---
  }

  // --- scheduler tracing related ---
  return root;
}
```

- `requestWork`の実行です
- `Sync`という変数には31ビット符号あり整数の最大値が入っています。
- `root-expirationTime`にも同様の数値が入っています。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberScheduler.js#L2084

```js

// isBatchingUpdates = false
// Sync              = 1073741823

// root           = root-ReactRoot
// expirationTime = root-expirationTime
function requestWork(root: FiberRoot, expirationTime: ExpirationTime) {
  addRootToSchedule(root, expirationTime);
  if (isRendering) {
    return;
  }

  if (isBatchingUpdates) {
    // --- process when batchingUpdates ---
    return;
  }

  if (expirationTime === Sync) {
    performSyncWork();
  } else {
    scheduleCallbackWithExpirationTime(root, expirationTime);
  }
}
```

- `addRootToSchedule`の実行です。ファイルのグローバル変数がごにょごにょされます。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberScheduler.js#L2112

```js

// firstScheduledRoot = null
// lastScheduledRoot  = null

// root           = root-ReactRoot
// expirationTime = root-expirationTime
function addRootToSchedule(root: FiberRoot, expirationTime: ExpirationTime) {
  if (root.nextScheduledRoot === null) {
    root.expirationTime = expirationTime;
    if (lastScheduledRoot === null) {
      firstScheduledRoot = lastScheduledRoot = root;
      root.nextScheduledRoot = root;
    } else {
      // --- process when lastScheduledRoot has value ---
    }
  } else {
    // --- process when root has nextScheduledRoot property ---
  }
}
```

##### 2-2-2-1 performWork

- `requestWork`に処理が戻りまして、`performSyncWork`の実行です。こちらは`performWork`のためのシンプルなラッパーです
- `performWork`では引数でデータが渡されなくなっているのですが、それはグローバル変数を頼りにしているからの模様です。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberScheduler.js#L2240
https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberScheduler.js#L2244

```js

function performSyncWork() {
  performWork(Sync, false);
}

// minExpirationTime = Sync
// isYieldy          = false
function performWork(minExpirationTime: ExpirationTime, isYieldy: boolean) {
  findHighestPriorityRoot();

  if (isYieldy) {
    // --- process when isYieldy ---
  } else {
    while (
      nextFlushedRoot !== null &&
      nextFlushedExpirationTime !== NoWork &&
      minExpirationTime <= nextFlushedExpirationTime
    ) {
      performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
      findHighestPriorityRoot();
    }
  }

  if (isYieldy) {
    // --- process when isYieldy ---
  }
  // If there's work left over, schedule a new callback.
  if (nextFlushedExpirationTime !== NoWork) {
    scheduleCallbackWithExpirationTime(
      ((nextFlushedRoot: any): FiberRoot),
      nextFlushedExpirationTime,
    );
  }

  // Clean-up.
  finishRendering();
}
```

- まずは`findHighestPriorityRoot`を実行して`nextFlushedRoot`と`nextFlushedExpirationTime`のお世話をします
- 実行後の値は以下です

```js

nextFlushedRoot           = root-ReactRoot
nextFlushedExpirationTime = root-expirationTime
```

- `performWorkOnRoot`の実行です

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberScheduler.js#L2349

```js

// root           = root-ReactRoot
// expirationTime = root-expirationTime
// isYieldy       = false
function performWorkOnRoot(
  root: FiberRoot,
  expirationTime: ExpirationTime,
  isYieldy: boolean,
) {
  // --- warning ---
  isRendering = true;

  if (!isYieldy) {
    let finishedWork = root.finishedWork;
    if (finishedWork !== null) {
      // --- process ---
    } else {
      root.finishedWork = null;
      const timeoutHandle = root.timeoutHandle;
      if (timeoutHandle !== noTimeout) {
        root.timeoutHandle = noTimeout;
        cancelTimeout(timeoutHandle);
      }
      renderRoot(root, isYieldy);
      finishedWork = root.finishedWork;
      if (finishedWork !== null) {
        completeRoot(root, finishedWork, expirationTime);
      }
    }
  } else {
    // --- process when isYieldy ---
  }

  isRendering = false;
}
```

###### 2-2-2-1-1 renderRoot

- renderRootの実行です
- DOMのreconcileや、propertyのセットなどこの関数の呼び出しの範囲内で実行されます。
- 途中でグローバル変数`nextUnitOfWork`を生成しています。
- ごちゃごちゃしてますが、真ん中あたりの`wookLoop`がほぼ本体です。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberScheduler.js#L1223

```js

// nextUnitOfWork = null

// root     = root-ReactRoot
// isYieldy = false
function renderRoot(root: FiberRoot, isYieldy: boolean): void {
  // --- warning ---

  flushPassiveEffects();

  isWorking = true;
  const previousDispatcher = ReactCurrentDispatcher.current;
  ReactCurrentDispatcher.current = ContextOnlyDispatcher;

  const expirationTime = root.nextExpirationTimeToWorkOn;

  if (
    expirationTime !== nextRenderExpirationTime ||
    root !== nextRoot ||
    nextUnitOfWork === null
  ) {
    // Reset the stack and start working from the root.
    resetStack();
    nextRoot = root;
    nextRenderExpirationTime = expirationTime;
    nextUnitOfWork = createWorkInProgress(
      nextRoot.current,
      null,
      nextRenderExpirationTime,
    );
    root.pendingCommitExpirationTime = NoWork;

    if (enableSchedulerTracing) {
      // --- process when schedulerTracing ---
    }
  }

  let prevInteractions: Set<Interaction> = (null: any);
  // --- process when schedulerTracing ---

  let didFatal = false;

  startWorkLoopTimer(nextUnitOfWork);

  do {
    try {
      workLoop(isYieldy);
    } catch (thrownValue) {
      // --- recovery process ---
    }
    break;
  } while (true);

  // --- process when schedulerTracing ---

  isWorking = false;
  ReactCurrentDispatcher.current = previousDispatcher;
  resetContextDependences();
  resetHooks();

  // Yield back to main thread.
  if (didFatal) {
    // --- process when fatal ---
    return;
  }

  if (nextUnitOfWork !== null) {
    const didCompleteRoot = false;
    stopWorkLoopTimer(interruptedBy, didCompleteRoot);
    interruptedBy = null;
    onYield(root);
    return;
  }

  const didCompleteRoot = true;
  stopWorkLoopTimer(interruptedBy, didCompleteRoot);
  const rootWorkInProgress = root.current.alternate;
  // --- warning ---

  nextRoot = null;
  interruptedBy = null;

  if (nextRenderDidError) {
    // --- process when error ---
  }

  if (isYieldy && nextLatestAbsoluteTimeoutMs !== -1) {
    // --- process when isYieldy ---
    return;
  }

  // Ready to commit.
  onComplete(root, rootWorkInProgress, expirationTime);
}
```

```js

// root-WorkInProgress

FiberNode {
  actualDuration: 0
  actualStartTime: -1
  alternate: root-HostRootFiber
  child: null
  childExpirationTime: 0
  contextDependencies: null
  effectTag: 0
  elementType: null
  expirationTime: 1073741823
  firstEffect: null
  index: 0
  key: null
  lastEffect: null
  memoizedProps: null
  memoizedState: null
  mode: 4
  nextEffect: null
  pendingProps: null
  ref: null
  return: null
  selfBaseDuration: 0
  sibling: null
  stateNode: root,
  tag: 3
  treeBaseDuration: 0
  type: null
  updateQueue: root-HostRootFiber.updateQueueと同じ
}
```

###### 2-2-2-1-1-1 performUnitOfWork

- 何はともあれwookLoopを実行します。
- `performUnitOfWork`の返り値がnullになるまで回し続けるだけのお仕事です。
- `beginWork`です！こいつは大事やぞ(多分)
- `workLoop`は3週します

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberScheduler.js#L1209

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberScheduler.js#L1147

```js

function workLoop(isYieldy) {
  if (!isYieldy) {
    while (nextUnitOfWork !== null) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  } else {
    // --- process when isYieldy ---
  }
}

// workInProgress = root-workInProgress
function performUnitOfWork(workInProgress: Fiber): Fiber | null {
  const current = workInProgress.alternate;

  startWorkTimer(workInProgress);
  // --- dev: process ---

  // --- dev: set dev property to fiber ---

  let next;
  if (enableProfilerTimer) {
    // --- process when profilerTimer ---
  } else {
    next = beginWork(current, workInProgress, nextRenderExpirationTime);
    workInProgress.memoizedProps = workInProgress.pendingProps;
  }

  // --- dev: process ---

  if (next === null) {
    next = completeUnitOfWork(workInProgress);
  }

  ReactCurrentOwner.current = null;

  return next;
}
```

###### 2-2-2-1-1-2 beginWork

- `beginWork`です。
- 今回の`workInProgress`が持っているタグは`HostRoot`にあたります。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberBeginWork.js#L1893

```js

// current              = root-HostRootFiber
// workInProgress       = root-workInProgress
// renderExpirationTime = root-expirationTime
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
): Fiber | null {
  const updateExpirationTime = workInProgress.expirationTime;

  if (current !== null) {
    const oldProps = current.memoizedProps;
    const newProps = workInProgress.pendingProps;

    if (oldProps !== newProps || hasLegacyContextChanged()) {
      // --- process when change occured ---
    } else if (updateExpirationTime < renderExpirationTime) {
      // --- process when there is no pending work ---
      return bailoutOnAlreadyFinishedWork(
        current,
        workInProgress,
        renderExpirationTime,
      );
    }
  } else {
    didReceiveUpdate = false;
  }

  // Before entering the begin phase, clear the expiration time.
  workInProgress.expirationTime = NoWork;

  switch (workInProgress.tag) {
    // --- other cases ---

    case HostRoot:
      return updateHostRoot(current, workInProgress, renderExpirationTime);

    // --- other cases ---
  }
  // --- warning ---
}
```

- `updateHostRoot`の実行です。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberBeginWork.js#L838

```js

// current              = root-HostRootFiber
// workInProgress       = root-workInProgress
// renderExpirationTime = root-expirationTime
function updateHostRoot(current, workInProgress, renderExpirationTime) {
  pushHostRootContext(workInProgress);
  const updateQueue = workInProgress.updateQueue;
  // --- warning ---
  const nextProps = workInProgress.pendingProps;
  const prevState = workInProgress.memoizedState;
  const prevChildren = prevState !== null ? prevState.element : null;
  processUpdateQueue(
    workInProgress,
    updateQueue,
    nextProps,
    null,
    renderExpirationTime,
  );
  const nextState = workInProgress.memoizedState;
  const nextChildren = nextState.element;
  // --- process when next/prev children are same ---
  const root: FiberRoot = workInProgress.stateNode;
  if (
    (current === null || current.child === null) &&
    root.hydrate &&
    enterHydrationState(workInProgress)
  ) {
    // --- other process ---
  } else {
    reconcileChildren(
      current,
      workInProgress,
      nextChildren,
      renderExpirationTime,
    );
    resetHydrationState();
  }
  // --- other process ---
}
```

- `processUpdateQueue`を実行してうぷで！

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactUpdateQueue.js#L416

```js

// workInProgress       = root-workInProgress
// queue     = root-workInProgress.updateQueue
// props                = null
// instance             = null
// renderExpirationTime = root-expirationTime
export function processUpdateQueue<State>(
  workInProgress: Fiber,
  queue: UpdateQueue<State>,
  props: any,
  instance: any,
  renderExpirationTime: ExpirationTime,
): void {
  hasForceUpdate = false;

  queue = ensureWorkInProgressQueueIsAClone(workInProgress, queue);

  // --- dev ---

  // These values may change as we process the queue.
  let newBaseState = queue.baseState;
  let newFirstUpdate = null;
  let newExpirationTime = NoWork;

  // Iterate through the list of updates to compute the result.
  let update = queue.firstUpdate;
  let resultState = newBaseState;
  while (update !== null) {
    const updateExpirationTime = update.expirationTime;
    if (updateExpirationTime < renderExpirationTime) {
      // --- process with insufficient expirationTime ---
    } else {
      resultState = getStateFromUpdate(
        workInProgress,
        queue,
        update,
        resultState,
        props,
        instance,
      );
      const callback = update.callback;
      if (callback !== null) {
        workInProgress.effectTag |= Callback;
        // Set this to null, in case it was mutated during an aborted render.
        update.nextEffect = null;
        if (queue.lastEffect === null) {
          queue.firstEffect = queue.lastEffect = update;
        } else {
          queue.lastEffect.nextEffect = update;
          queue.lastEffect = update;
        }
      }
    }
    // Continue to the next update.
    update = update.next;
  }

  // --- process when queue has capturedUpdate ---

  if (newFirstUpdate === null) {
    queue.lastUpdate = null;
  }
  if (newFirstCapturedUpdate === null) {
    queue.lastCapturedUpdate = null;
  } else {
    workInProgress.effectTag |= Callback;
  }
  if (newFirstUpdate === null && newFirstCapturedUpdate === null) {
    newBaseState = resultState;
  }

  queue.baseState = newBaseState;
  queue.firstUpdate = newFirstUpdate;
  queue.firstCapturedUpdate = newFirstCapturedUpdate;

  workInProgress.expirationTime = newExpirationTime;
  workInProgress.memoizedState = resultState;

  // --- dev ---
}
```

- `getStateFromUpdate`です。今回の`tag`は`UpdateState`。
- 今回は実質`update.payload`を新しいオブジェクトに展開するだけです。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactUpdateQueue.js#L341

```js

// workInProgress       = root-workInProgress
// queue                = root-workInProgress.updateQueue
// update               = root-update
// prevState            = null
// props                = null
// instance             = null
function getStateFromUpdate<State>(
  workInProgress: Fiber,
  queue: UpdateQueue<State>,
  update: Update<State>,
  prevState: State,
  nextProps: any,
  instance: any,
): any {
  switch (update.tag) {
    // --- other cases ---
    case UpdateState: {
      const payload = update.payload;
      let partialState;
      if (typeof payload === 'function') {
        // --- process when payload is function ---
      } else {
        partialState = payload;
      }
      if (partialState === null || partialState === undefined) {
        return prevState;
      }
      return Object.assign({}, prevState, partialState);
    }
    // --- other cases ---
  }
  return prevState;
}
```

```js

// root-workInProgress
FiberNode {
  actualDuration: 0
  actualStartTime: 59901.585000000065
  alternate: root-HostRootFiber
  child: null
  childExpirationTime: 0
  contextDependencies: null
  effectTag: 32
  elementType: null
  expirationTime: 0
  firstEffect: null
  index: 0
  key: null
  lastEffect: null
  memoizedProps: null
  memoizedState: {element: {…}}
  mode: 4
  nextEffect: null
  pendingProps: null
  ref: null
  return: null
  selfBaseDuration: 0
  sibling: null
  stateNode: root-internalRoot
  tag: 3
  treeBaseDuration: 0
  type: null
  updateQueue: {baseState: {…}, firstUpdate: null, lastUpdate: null, firstCapturedUpdate: null, lastCapturedUpdate: null, …}
}
```

```js

// root-workInProgress.updateQueue
{
  baseState: {element: root-ReactElement}
  firstCapturedEffect: null
  firstCapturedUpdate: null
  firstEffect: root-update
  firstUpdate: null
  lastCapturedEffect: null
  lastCapturedUpdate: null
  lastEffect: root-update
  lastUpdate: null
}
```

- `reconcileChildren`の実行で、Childrenを良きようにします。
- `child`のプロパティの中身は後ほど示します。

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberBeginWork.js#L151

```js

// current              = root-HostRootFiber
// workInProgress       = root-workInProgress
// nextChildren         = root-ReactElement
// renderExpirationTime = root-expirationTime
export function reconcileChildren(
  current: Fiber | null,
  workInProgress: Fiber,
  nextChildren: any,
  renderExpirationTime: ExpirationTime,
) {
  if (current === null) {
    // --- other process ---
  } else {
    workInProgress.child = reconcileChildFibers(
      workInProgress,
      current.child,
      nextChildren,
      renderExpirationTime,
    );
  }
}
```

- `reconcileChildFibers`
- このwrapperはなんか最適化のためらしいけどもどういうことか・・・

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactChildFiber.js#L1343

```js

export const reconcileChildFibers = ChildReconciler(true);

function ChildReconciler(shouldTrackSideEffects) {
  // --- other functions ---

  function placeSingleChild(newFiber: Fiber): Fiber {
    // This is simpler for the single child case. We only need to do a
    // placement for inserting new children.
    if (shouldTrackSideEffects && newFiber.alternate === null) {
      newFiber.effectTag = Placement;
    }
    return newFiber;
  }

  function reconcileSingleElement () {}

  // --- other functions ---

  // returnFiber       = root-HostRootFiber
  // currentFirstChild = null
  // newChild          = root-ReactElement
  // expirationTime    = root-expirationTime
  function reconcileChildFibers(
    returnFiber: Fiber,
    currentFirstChild: Fiber | null,
    newChild: any,
    expirationTime: ExpirationTime,
  ): Fiber | null {
    const isUnkeyedTopLevelFragment =
      typeof newChild === 'object' &&
      newChild !== null &&
      newChild.type === REACT_FRAGMENT_TYPE &&
      newChild.key === null;
    if (isUnkeyedTopLevelFragment) {
      newChild = newChild.props.children;
    }

    // Handle object types
    const isObject = typeof newChild === 'object' && newChild !== null;

    if (isObject) {
      switch (newChild.$$typeof) {
        case REACT_ELEMENT_TYPE:
          return placeSingleChild(
            reconcileSingleElement(
              returnFiber,
              currentFirstChild,
              newChild,
              expirationTime,
            ),
          );
        // --- other case ---
      }
    }

    // --- other process ---
  }

  // --- other process ---
}
```

```js

// returnFiber          = root-workInProgress
// currentFirstChild    = null
// element              = root-ReactElement
// renderExpirationTime = root-expirationTime
function reconcileSingleElement(
  returnFiber: Fiber,
  currentFirstChild: Fiber | null,
  element: ReactElement,
  expirationTime: ExpirationTime,
): Fiber {
  const key = element.key;
  let child = currentFirstChild;
  while (child !== null) {
    // --- process when child is not null ---
  }

  if (element.type === REACT_FRAGMENT_TYPE) {
    // --- process when fragment ---
  } else {
    const created = createFiberFromElement(
      element,
      returnFiber.mode,
      expirationTime,
    );
    created.ref = coerceRef(returnFiber, currentFirstChild, element);
    created.return = returnFiber;
    return created;
  }
}
```

- `createFiberFromElement`を実行して`Fiber`を生成します

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiber.js#L540

```js

// element        = root-ReactElement
// mode           = 4
// expirationTime = root-expirationTime
export function createFiberFromElement(
  element: ReactElement,
  mode: TypeOfMode,
  expirationTime: ExpirationTime,
): Fiber {
  let owner = null;
  // --- dev: process for debug ---
  const type = element.type;
  const key = element.key;
  const pendingProps = element.props;
  const fiber = createFiberFromTypeAndProps(
    type,
    key,
    pendingProps,
    owner,
    mode,
    expirationTime,
  );
  // --- dev: process for debug ---
  return fiber;
}
```

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiber.js#L434

```js

// type           = App
// key            = null
// pendingProps   = {}
// owner          = null
// mode           = 4
// expirationTime = root-expirationTime
export function createFiberFromTypeAndProps(
  type: any, // React$ElementType
  key: null | string,
  pendingProps: any,
  owner: null | Fiber,
  mode: TypeOfMode,
  expirationTime: ExpirationTime,
): Fiber {
  let fiber;

  let fiberTag = IndeterminateComponent;
  let resolvedType = type;
  if (typeof type === 'function') {
    if (shouldConstruct(type)) {
      // --- when its class Component ---
    }
  } else if (typeof type === 'string') {
    // --- when its HostComponent
  } else {
    // --- when else ---
  }

  fiber = createFiber(fiberTag, pendingProps, key, mode);
  fiber.elementType = type;
  fiber.type = resolvedType;
  fiber.expirationTime = expirationTime;

  return fiber;
}
```

- 生成された`Fiber`はこちらです。`reconcileChildren`の返り値で`root-workInProgress.child`の中身になります。
- 以降`root-childFiber`と呼びます

```js

// root-childFiber

FiberNode {
  actualDuration: 0
  actualStartTime: -1
  alternate: null
  child: null
  childExpirationTime: 0
  contextDependencies: null
  effectTag: 2
  elementType: ƒ App(props)
  expirationTime: 1073741823
  firstEffect: null
  index: 0
  key: null
  lastEffect: null
  memoizedProps: null
  memoizedState: null
  mode: 4
  nextEffect: null
  pendingProps: {}
  ref: null
  return: root-workInProgress
  selfBaseDuration: 0
  sibling: null
  stateNode: null
  tag: 2
  treeBaseDuration: 0
  type: ƒ App(props)
  updateQueue: null
}
```

- call履歴をはるか遡って、`workLoop`の2週目まで戻ってきます。

```js

// nextUnitOfWork = root-childFiber

// isYiedly = false
function workLoop(isYieldy) {
  if (!isYieldy) {
    while (nextUnitOfWork !== null) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  } else {
    // --- other process ---
  }
}
```

- 以降差分のみになります

```js

// current              = null
// workInProgress       = root-childFiber
// renderExpirationTime = root-expirationTime
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
): Fiber | null {
  const updateExpirationTime = workInProgress.expirationTime;

  if (current !== null) {
    // --- process when not null ---
  } else {
    didReceiveUpdate = false;
  }

  workInProgress.expirationTime = NoWork;

  switch (workInProgress.tag) {
    case IndeterminateComponent: {
      const elementType = workInProgress.elementType;
      return mountIndeterminateComponent(
        current,
        workInProgress,
        elementType,
        renderExpirationTime,
      );
    }
    // --- other cases ---
  }
}
```

- `mountIndeterminateComponent`の実行です。
- まず`renderWithHooks`を実行して`value`の値を求めます。
- その後再びの`reconcileChildren`でchildrenの状態を求めます。

https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react-reconciler/src/ReactFiberBeginWork.js#L1128

```js

// _current             = null
// workInProgress       = root-childFiber
// Component            = App
// renderExpirationTime = root-expirationTime
function mountIndeterminateComponent(
  _current,
  workInProgress,
  Component,
  renderExpirationTime,
) {
  if (_current !== null) {
    // --- process when current not null ---
  }

  const props = workInProgress.pendingProps;
  const unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
  const context = getMaskedContext(workInProgress, unmaskedContext);

  prepareToReadContext(workInProgress, renderExpirationTime);

  let value;

  if (__DEV__) {
    // --- dev ---
  } else {
    value = renderWithHooks(
      null,
      workInProgress,
      Component,
      props,
      context,
      renderExpirationTime,
    );
  }
  workInProgress.effectTag |= PerformedWork;

  if (
    typeof value === 'object' &&
    value !== null &&
    typeof value.render === 'function' &&
    value.$$typeof === undefined
  ) {
    // --- process for class component ---
  } else {
    workInProgress.tag = FunctionComponent;
    // --- dev ---
    reconcileChildren(null, workInProgress, value, renderExpirationTime);
    // --- dev ---
    return workInProgress.child;
  }
}
```

- `renderWithHooks`です。
- なんと！ついにこちらで関数呼び出しが行われております！こういうのが見たかった
- 中盤あたりで出てくる`renderedWork`はなんのために宣言されているのだろうか・・・

https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react-reconciler/src/ReactFiberHooks.js#L286

```js

// current                  = null
// workInProgress           = root-childFiber
// Component                = App
// props                    = {}
// context                  = {}
// nextRenderExpirationTime = root-expirationTime
export function renderWithHooks(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  props: any,
  refOrContext: any,
  nextRenderExpirationTime: ExpirationTime,
): any {
  renderExpirationTime = nextRenderExpirationTime;
  currentlyRenderingFiber = workInProgress;
  firstCurrentHook = nextCurrentHook =
    current !== null ? current.memoizedState : null;

  if (__DEV__) {
    // --- dev ---
  } else {
    ReactCurrentDispatcher.current =
      nextCurrentHook === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;
  }

  let children = Component(props, refOrContext);

  if (didScheduleRenderPhaseUpdate) {
    do {
      // --- process when update ---
    } while (didScheduleRenderPhaseUpdate);

    renderPhaseUpdates = null;
    numberOfReRenders = 0;
  }

  // --- dev ---

  ReactCurrentDispatcher.current = ContextOnlyDispatcher;

  const renderedWork: Fiber = (currentlyRenderingFiber: any);

  renderedWork.memoizedState = firstWorkInProgressHook;
  renderedWork.expirationTime = remainingExpirationTime;
  renderedWork.updateQueue = (componentUpdateQueue: any);
  renderedWork.effectTag |= sideEffectTag;

  const didRenderTooFewHooks =
    currentHook !== null && currentHook.next !== null;

  renderExpirationTime = NoWork;
  currentlyRenderingFiber = null;

  firstCurrentHook = null;
  currentHook = null;
  nextCurrentHook = null;
  firstWorkInProgressHook = null;
  workInProgressHook = null;
  nextWorkInProgressHook = null;

  remainingExpirationTime = NoWork;
  componentUpdateQueue = null;
  sideEffectTag = 0;

  // --- warning ---

  return children;
}
```

- 生成された`children`はこちら
- 以降`root-AppChildrenReactElement`と呼びます

```js

// root-AppChildrenReactElement

{
  $$typeof: Symbol(react.element)
  key: null
  props: {children: "Hello world"}
  ref: null
  type: "div"
}
```

- 再び`reconcileChildren`の実行です。

```js

// current              = null
// workInProgress       = root-childFiber
// nextChildren         = root-AppChildrenReactElement
// renderExpirationTime = root-expirationTime
export function reconcileChildren(
  current: Fiber | null,
  workInProgress: Fiber,
  nextChildren: any,
  renderExpirationTime: ExpirationTime,
) {
  if (current === null) {
    workInProgress.child = mountChildFibers(
      workInProgress,
      null,
      nextChildren,
      renderExpirationTime,
    );
  } else {
    // --- other process ---
  }
}
```

```js

// root-childFiber.child

FiberNode {
  actualDuration: 0
  actualStartTime: -1
  alternate: null
  child: null
  childExpirationTime: 0
  contextDependencies: null
  effectTag: 0
  elementType: "div"
  expirationTime: 1073741823
  firstEffect: null
  index: 0
  key: null
  lastEffect: null
  memoizedProps: null
  memoizedState: null
  mode: 4
  nextEffect: null
  pendingProps: {children: "Hello world"}
  ref: null
  return: root-workInProgress
  selfBaseDuration: 0
  sibling: null
  stateNode: null
  tag: 5
  treeBaseDuration: 0
  type: "div"
  updateQueue: null
}
```

- 3週目の`workLoop`です。

```js

// nextUnitOfWork = root-childFiber.child

function workLoop(isYieldy) {
  if (!isYieldy) {
    while (nextUnitOfWork !== null) {
      nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
    }
  } else {
    // --- process when isYieldy ---
  }
}
```

```js

// current              = null
// workInProgress       = root-childFiber.child
// renderExpirationTime = root-expirationTime
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
): Fiber | null {
  const updateExpirationTime = workInProgress.expirationTime;

  if (current !== null) {
    // --- other process ---
  } else {
    didReceiveUpdate = false;
  }

  workInProgress.expirationTime = NoWork;

  switch (workInProgress.tag) {
    // --- other cases ---
    case HostComponent:
      return updateHostComponent(current, workInProgress, renderExpirationTime);
    // --- other cases ---
  }
}
```

https://github.com/facebook/react/blob/08e95543571eacbe88a03382adc9399607d53425/packages/react-reconciler/src/ReactFiberBeginWork.js#L911

```js

// current              = null
// workInProgress       = root-childFiber.child
// renderExpirationTime = root-expirationTime
function updateHostComponent(current, workInProgress, renderExpirationTime) {
  pushHostContext(workInProgress);

  if (current === null) {
    tryToClaimNextHydratableInstance(workInProgress);
  }

  const type = workInProgress.type;
  const nextProps = workInProgress.pendingProps;
  const prevProps = current !== null ? current.memoizedProps : null;

  let nextChildren = nextProps.children;
  const isDirectTextChild = shouldSetTextContent(type, nextProps);

  if (isDirectTextChild) {
    nextChildren = null;
  } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
    // --- process when content change ---
  }

  markRef(current, workInProgress);

  if (
    renderExpirationTime !== Never &&
    workInProgress.mode & ConcurrentMode &&
    shouldDeprioritizeSubtree(type, nextProps)
  ) {
    workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
    return null;
  }

  reconcileChildren(
    current,
    workInProgress,
    nextChildren,
    renderExpirationTime,
  );
  return workInProgress.child;
}
```

- 上記までを実行して`workInProgress.child`の中身は`null`になっています。
- のでこの3週目の`performUnitOfWork`で初めて`completeUnitOfWork`が実行されます。

```js

// workInProgress = root-childFiber.child
function performUnitOfWork(workInProgress: Fiber): Fiber | null {
  // --- truncate ---

  let next;
  if (enableProfilerTimer) {
    // --- process ----
  } else {
    next = beginWork(current, workInProgress, nextRenderExpirationTime);
    workInProgress.memoizedProps = workInProgress.pendingProps;
  }

  // --- dev ---

  if (next === null) {
    // If this doesn't spawn new work, complete the current work.
    next = completeUnitOfWork(workInProgress);
  }

  ReactCurrentOwner.current = null;

  return next;
}
```

###### 2-2-2-1-1-3 completeUnitOfWork

https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react-reconciler/src/ReactFiberScheduler.js#L954

```js

// workInProgress = root-childFiber.child
function completeUnitOfWork(workInProgress: Fiber): Fiber | null {
  while (true) {
    const current = workInProgress.alternate;
    // --- dev ---

    const returnFiber = workInProgress.return;
    const siblingFiber = workInProgress.sibling;

    if ((workInProgress.effectTag & Incomplete) === NoEffect) {
      // --- dev ---
      nextUnitOfWork = workInProgress;
      if (enableProfilerTimer) {
        if (workInProgress.mode & ProfileMode) {
          startProfilerTimer(workInProgress);
        }
        nextUnitOfWork = completeWork(
          current,
          workInProgress,
          nextRenderExpirationTime,
        );
        if (workInProgress.mode & ProfileMode) {
          stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
        }
      } else {
        // --- other process ---
      }
      // --- dev ---
      stopWorkTimer(workInProgress);
      resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
      // --- dev ---

      if (nextUnitOfWork !== null) {
        return nextUnitOfWork;
      }

      if (
        returnFiber !== null &&
        (returnFiber.effectTag & Incomplete) === NoEffect
      ) {
        if (returnFiber.firstEffect === null) {
          returnFiber.firstEffect = workInProgress.firstEffect;
        }
        if (workInProgress.lastEffect !== null) {
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
          }
          returnFiber.lastEffect = workInProgress.lastEffect;
        }

        const effectTag = workInProgress.effectTag;
        if (effectTag > PerformedWork) {
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = workInProgress;
          } else {
            returnFiber.firstEffect = workInProgress;
          }
          returnFiber.lastEffect = workInProgress;
        }
      }

      // --- dev ---

      if (siblingFiber !== null) {
        // --- when sibling ---
      } else if (returnFiber !== null) {
        workInProgress = returnFiber;
        continue;
      } else {
        // --- when reached root ---
      }
    } else {
      // --- other process ---
    }
  }

  return null;
}
```

- `App`関数内の`<div>Hello world</div>`向けの`completeWork`の実行です
- `ReactFiberHostConfig`というファイルから読み込まれた関数を実行しているのですが、そのファイルの実態は`ReactDOM`のpackageに含まれていて、参照のためにrollupがごにょごにょと頑張るらしいです。


https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react-reconciler/src/ReactFiberCompleteWork.js#L539

```js

// current              = null
// workInprogress       = root-childFiber.child
// renderExpirationTime = root-expirationTime
function completeWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
): Fiber | null {
  const newProps = workInProgress.pendingProps;

  switch (workInProgress.tag) {
    // --- other cases ---
    case HostComponent: {
      popHostContext(workInProgress);
      const rootContainerInstance = getRootHostContainer();
      const type = workInProgress.type;
      if (current !== null && workInProgress.stateNode != null) {
        // --- other process ---
      } else {
        if (!newProps) {
          // --- process when without props
          break;
        }

        const currentHostContext = getHostContext();
        let wasHydrated = popHydrationState(workInProgress);
        if (wasHydrated) {
          // --- process when hydrated ---
        } else {
          let instance = createInstance(
            type,
            newProps,
            rootContainerInstance,
            currentHostContext,
            workInProgress,
          );

          appendAllChildren(instance, workInProgress, false, false);

          if (
            finalizeInitialChildren(
              instance,
              type,
              newProps,
              rootContainerInstance,
              currentHostContext,
            )
          ) {
            markUpdate(workInProgress);
          }
          workInProgress.stateNode = instance;
        }

        if (workInProgress.ref !== null) {
          markRef(workInProgress);
        }
      }
      break;
    }
    // --- other cases ---
  }

  return null;
}
```

- `createInstance`です
- `precacheFiberNode`と`updateFiberProps`の実行がこちらで有りますが、どうも実DOMのプロパティにfiberとpropsの参照を持たせているようです。これは知らなかった。どっかで使われるんだろうか

https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react-dom/src/client/ReactDOMHostConfig.js#L174

```js

// type                   = "div"
// props                  = { children: "Hello world"}
// rootContainerInstance  = div#app-root
// hostContext            = {namespace: "http://www.w3.org/1999/xhtml", ancestorInfo: {…}}
// internalInstanceHandle = root-childFiber.child
export function createInstance(
  type: string,
  props: Props,
  rootContainerInstance: Container,
  hostContext: HostContext,
  internalInstanceHandle: Object,
): Instance {
  let parentNamespace: string;
  if (__DEV__) {
    // --- dev ---
  } else {
    parentNamespace = ((hostContext: any): HostContextProd);
  }
  const domElement: Instance = createElement(
    type,
    props,
    rootContainerInstance,
    parentNamespace,
  );
  precacheFiberNode(internalInstanceHandle, domElement);
  updateFiberProps(domElement, props);
  return domElement;
}
```

- ついに`document.createElement`が実行されております。こちらにおりましたか。。
- 生成された`domElement`は以降`root-AppChildDOM`と呼びます

https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react-dom/src/client/ReactDOMComponent.js#L374

```js

// type = 'div'
// props = { children: "Hello world" }
// rootContainerElement = div#app-root
// parentNameSpace = "http://www.w3.org/1999/xhtml"
export function createElement(
  type: string,
  props: Object,
  rootContainerElement: Element | Document,
  parentNamespace: string,
): Element {
  let isCustomComponentTag;

  const ownerDocument: Document = getOwnerDocumentFromRootContainer(
    rootContainerElement,
  );
  let domElement: Element;
  let namespaceURI = parentNamespace;
  if (namespaceURI === HTML_NAMESPACE) {
    namespaceURI = getIntrinsicNamespace(type);
  }
  if (namespaceURI === HTML_NAMESPACE) {
    // --- dev: warning ---

    if (type === 'script') {
      // --- when type is script ---
    } else if (typeof props.is === 'string') {
      // --- when its web components? ---
    } else {
      domElement = ownerDocument.createElement(type);
      // --- process for select tag ---
    }
  } else {
    // --- process for namespaced element ---
  }

  if (__DEV__) {
    // --- dev: warning ---
  }

  return domElement;
}
```

- `completeWork`の次の実行に移りまして`appendAllChildren`の実行です
- 今回の`workInProgress`はchildがnullなのでwhile内は実行しません

https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react-reconciler/src/ReactFiberCompleteWork.js#L103

```js

// parent                = root-AppChildDOM
// workInProgress        = root-childFiber.child
// needsVisibilityToggle = false
// isHidden              = false
appendAllChildren = function(
    parent: Instance,
    workInProgress: Fiber,
    needsVisibilityToggle: boolean,
    isHidden: boolean,
  ) {
    let node = workInProgress.child;
    while (node !== null) {
      // --- process ---
    }
  };
```

- `completeWork`の次の`finalizeInitialChildren`の実行です

https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react-dom/src/client/ReactDOMHostConfig.js#L219

```js

// domElement            = root-AppChildDOM
// type                  = "div"
// props                 = { children: "Hello world" }
// rootContainerInstance = div#app-root
// hostContext           = {namespace: "http://www.w3.org/1999/xhtml", ancestorInfo: {…}}
export function finalizeInitialChildren(
  domElement: Instance,
  type: string,
  props: Props,
  rootContainerInstance: Container,
  hostContext: HostContext,
): boolean {
  setInitialProperties(domElement, type, props, rootContainerInstance);
  return shouldAutoFocusHostComponent(type, props);
}
```

https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react-dom/src/client/ReactDOMComponent.js#L468

```js

// domElement            = root-AppChildDOM
// type                  = "div"
// props                 = { children: "Hello world" }
// rootContainerInstance = div#app-root
export function setInitialProperties(
  domElement: Element,
  tag: string,
  rawProps: Object,
  rootContainerElement: Element | Document,
): void {
  const isCustomComponentTag = isCustomComponent(tag, rawProps);
  // --- dev: warning ---

  let props: Object;
  switch (tag) {
    // --- other cases ---
    default:
      props = rawProps;
  }

  assertValidProps(tag, props);

  setInitialDOMProperties(
    tag,
    domElement,
    rootContainerElement,
    props,
    isCustomComponentTag,
  );

  switch (tag) {
    // --- process input related tag ---
    default:
      if (typeof props.onClick === 'function') {
        trapClickOnNonInteractiveElement(((domElement: any): HTMLElement));
      }
      break;
  }
}
```

https://github.com/facebook/react/blob/f24a0da6e0f59484e5aafd0825bb1a6ed27d7182/packages/react-dom/src/client/ReactDOMComponent.js#L289

```js

// tag                  = "div"
// domElement           = root-AppChildDOM
// rootContainerElement = div#app-root
// nextProps            = { children: "Hello world" }
// isCustomComponentTag = false
function setInitialDOMProperties(
  tag: string,
  domElement: Element,
  rootContainerElement: Element | Document,
  nextProps: Object,
  isCustomComponentTag: boolean,
): void {
  for (const propKey in nextProps) {
    if (!nextProps.hasOwnProperty(propKey)) {
      continue;
    }
    const nextProp = nextProps[propKey];
    if (propKey === STYLE) {
      // --- process when key is style ---
    } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
      // --- process when key is dangerouslySetInnerHtml ---
    } else if (propKey === CHILDREN) {
      if (typeof nextProp === 'string') {
        const canSetTextContent = tag !== 'textarea' || nextProp !== '';
        if (canSetTextContent) {
          setTextContent(domElement, nextProp);
        }
      } else if (typeof nextProp === 'number') {
        // --- process when props is number ---
      }
    } else if (
      // --- other cases ---
    }
  }
}
```

- ここまできて、`completeUnitOfWork`の`completeWork`の実行が終わり、その後何やかんやがあって`completeUnitOfWork`のwhileをもう2週ほどするのですが、やんごとなき事情により割愛します
- 呼び出し履歴をはるか戻って`renderRoot`の`workLoop`呼び出し部分が完了してその続きです。
- `onComplete`を呼び出して、`root`の`expirationTime`の更新と`finishedWork`の更新をしています


```js

// nextUnitOfWork = null

// root     = root-ReactRoot
// isYieldy = false
function renderRoot(root: FiberRoot, isYieldy: boolean): void {
  // --- previous process ---
  do {
    try {
      workLoop(isYieldy);
    } catch (thrownValue) {
      // --- recovery process ---
    }
    break;
  } while (true);

  // --- process when schedulerTracing ---

  isWorking = false;
  ReactCurrentDispatcher.current = previousDispatcher;
  resetContextDependences();
  resetHooks();

  // Yield back to main thread.
  if (didFatal) {
    // --- process when fatal ---
    return;
  }

  if (nextUnitOfWork !== null) {
    // --- process ---
    return;
  }

  const didCompleteRoot = true;
  stopWorkLoopTimer(interruptedBy, didCompleteRoot);
  const rootWorkInProgress = root.current.alternate;
  // --- warning ---

  nextRoot = null;
  interruptedBy = null;

  if (nextRenderDidError) {
    // --- process when error ---
  }

  if (isYieldy && nextLatestAbsoluteTimeoutMs !== -1) {
    // --- process when isYieldy ---
    return;
  }

  // Ready to commit.
  onComplete(root, rootWorkInProgress, expirationTime);
}
```

##### 2-2-2-1-2 completeRoot

- ついについに`renderRoot`の実行が終了しまして、`performWorkOnRoot`に処理が戻ります

```js:再喝

// root           = root-ReactRoot
// expirationTime = root-expirationTime
// isYieldy       = false
function performWorkOnRoot(
  root: FiberRoot,
  expirationTime: ExpirationTime,
  isYieldy: boolean,
) {
  // --- warning ---
  isRendering = true;

  if (!isYieldy) {
    let finishedWork = root.finishedWork;
    if (finishedWork !== null) {
      // --- process ---
    } else {
      // --- previous process ---
      renderRoot(root, isYieldy);
      finishedWork = root.finishedWork;
      if (finishedWork !== null) {
        completeRoot(root, finishedWork, expirationTime);
      }
    }
  } else {
    // --- process when isYieldy ---
  }

  isRendering = false;
}
```