Reactのコードを読む(2)
前回は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
などをみていきたい。