前回はReactDOMについてみたので、今回はrenderでhello worldが描写されるところまでみていこうと思う。
まずは、ReactDom.renderを出力してみる。
<!DOCTYPE html> <html> <head> <script src="https://unpkg.com/react@17/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script> </head> <body> </div> <script> console.log(ReactDOM.render()); </script> </body> </html>
すると、以下のようなエラーが発生する。
Uncaught Error: Target container is not a DOM element.
at Object.render (react-dom.development.js:26121)
at index.html:11エラーが発生しているのは26121行目で、!isValidContainer(container)がtrueになっている。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L26121
function render(element, container, callback) { if (!isValidContainer(container)) { { throw Error( "Target container is not a DOM element." ); } } ...
isValidContainer関数は25932行目にある。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L25932
function isValidContainer(node) { return !!(node && (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE || node.nodeType === COMMENT_NODE && node.nodeValue === ' react-mount-point-unstable ')); }
今回は引数のnodeがundefinedであるため、falseが返り、!isValidContainer(container)はtrueとなる。
では、以下のコードで実際にhello worldを表示させた状態でみていく。
<div id="root"></div> <script> console.log(ReactDOM.render( "hello world", document.getElementById('root') )); </script> </body> </html>
render関数の第二引数であるdocument.getElementById('root')のnodeTypeを出力すると、1であった。
console.log(document.getElementById('root').nodeType); // 1
Node.nodeTypeが1ということはつまりELEMENT_NODEであるから、isValidContainer関数はtrueを返す。
続いて、26126行目をみてみる。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L26126
var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined;
まずは、isContainerMarkedAsRoot関数が定義されている10597行目をみてみる。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L10597
function isContainerMarkedAsRoot(node) { return !!node[internalContainerInstanceKey]; }
internalContainerInstanceKeyは、10583行目で生成されているrandomKeyを含む変数であり、10586行目に定義されている。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L10583
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L10586
var randomKey = Math.random().toString(36).slice(2); ... var internalContainerInstanceKey = '__reactContainer$' + randomKey;
よって、最終的に例えばnode["__reactContainer$dgg96aihpip"]の存在が調べられ、falseが返り、26110行目の条件分岐は通らない。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L26110
if (isModernRoot) { error('You are calling ReactDOM.hydrate() on a container that was previously ' + 'passed to ReactDOM.createRoot(). This is not supported. ' + 'Did you mean to call createRoot(container, {hydrate: true}).render(element)?'); }
続く26133行目では、関数の返り値を返している。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L26133
return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
legacyRenderSubtreeIntoContainer関数は26024行目に定義されており、その中で26032行目にrootが定義されている。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L26032
var root = container._reactRootContainer;
div #rootには_reactRootContainerは存在せず、rootはundefinedになる。
よって、26035行目のif (!root)を通り、26037行目でrootが代入される。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L26037
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
legacyCreateRootFromDOMContainerは25983行目に定義されており、26011行目で関数の返り値を返している。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L26011
return createLegacyRoot(container, shouldHydrate ? { hydrate: true } : undefined);
そのcreateLegacyRootは25929行目に定義されており、25930行目でインスタンスを返している。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L25930
return new ReactDOMBlockingRoot(container, LegacyRoot, options);
結果的に、rootは以下のような値になる。
_internalRoot: FiberRootNode
callbackNode: null
callbackPriority: 0
containerInfo: div#root
context: null
current: FiberNode {tag: 3, key: null, elementType: null, type: null, stateNode: FiberRootNode, …}
entangledLanes: 0
entanglements: (31) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
....fiberRootの値はroot._internalRootであり、以下のようになる。
callbackNode: null
callbackPriority: 0
containerInfo: div#root
context: null
current: FiberNode {tag: 3, key: null, elementType: null, type: null, stateNode: FiberRootNode, …}
entangledLanes: 0
entanglements: (31) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
eventTimes: (31) [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
expirationTimes: (31) [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]このcontainerInfo: div#rootには、document.getElementById('root')で取得したDOMの情報が含まれている。
... outerHTML: "<div id="root"></div>" outerText: "" ownerDocument: document URL: "file:///Users/tagawahirotaka/Desktop/react-17-react-dom-17/index.html" ...
今までみてきたlegacyRenderSubtreeIntoContainerは、最終的に26070行目で関数の返り値を返している。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L26070
return getPublicRootInstance(fiberRoot);
getPublicRootInstanceは25515行目で定義されており、25527行目でNodeを返している。
https://github.com/wafuwafu13/react-17-react-dom-17/blob/master/react-dom.development.js#L25527
return containerFiber.child.stateNode;
このcontainerFiber.child.stateNodeには、renderの第一引数で渡したhello worldがnodeValueやtextContentとして含まれている。
nodeName: "#text" nodeType: 3 nodeValue: "hello world" ownerDocument: document parentElement: div#root parentNode: div#root previousElementSibling: null previousSibling: null textContent: "hello world"
このNodeが返されることにより、画面にhello worldが描写される。
途中の処理を結構飛ばしたので理解が曖昧だが、次はuseStateなどをみていきたい。