Files
react/contributing/implementation-notes.html
T
Dan Abramov eb0967749c Rebuild
2017-09-26 18:18:22 +01:00

1217 lines
100 KiB
HTML

<!DOCTYPE html>
<!--[if IE]><![endif]-->
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Implementation Notes - React</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta property="og:title" content="Implementation Notes - React">
<meta property="og:type" content="website">
<meta property="og:url" content="https://facebook.github.io/react/contributing/implementation-notes.html">
<meta property="og:image" content="https://facebook.github.io/react/img/logo_og.png">
<meta property="og:description" content="A JavaScript library for building user interfaces">
<meta property="fb:app_id" content="623268441017527">
<link rel="shortcut icon" href="/react/favicon.ico">
<link rel="alternate" type="application/rss+xml" title="React" href="https://facebook.github.io/react/feed.xml">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/1/docsearch.min.css" />
<link rel="stylesheet" href="/react/css/syntax.css">
<link rel="stylesheet" href="/react/css/codemirror.css">
<link rel="stylesheet" href="/react/css/react.css">
<script src="//use.typekit.net/vqa1hcx.js"></script>
<script>try{Typekit.load();}catch(e){}</script>
<!--[if lte IE 8]>
<script src="https://unpkg.com/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
<script src="https://unpkg.com/es5-shim@4.5.9/es5-shim.min.js"></script>
<script src="https://unpkg.com/es5-shim@4.5.9/es5-sham.min.js"></script>
<![endif]-->
<script src="https://unpkg.com/docsearch.js@1.5.0/dist/cdn/docsearch.min.js"></script>
<script src="https://unpkg.com/codemirror@5.15.2"></script>
<script src="https://unpkg.com/codemirror@5.15.2/mode/javascript/javascript.js"></script>
<script src="https://unpkg.com/codemirror@5.15.2/mode/xml/xml.js"></script>
<script src="https://unpkg.com/codemirror@5.15.2/mode/jsx/jsx.js"></script>
<script src="https://unpkg.com/prop-types/prop-types.min.js"></script>
<script src="https://unpkg.com/react/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/create-react-class/create-react-class.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script src="/react/js/live_editor.js"></script>
</head>
<body>
<div class="container">
<div class="nav-main">
<div class="wrap">
<a class="nav-home" href="/react/">
<img class="nav-logo" src="/react/img/logo.svg" width="36" height="36">
React
</a>
<div class="nav-lists">
<ul class="nav-site nav-site-internal">
<li><a href="/react/docs/hello-world.html" class="active">Docs</a></li>
<li><a href="/react/tutorial/tutorial.html">Tutorial</a></li>
<li><a href="/react/community/support.html">Community</a></li>
<li><a href="/react/blog/">Blog</a></li>
<li class="nav-site-search">
<input id="algolia-doc-search" type="text" placeholder="Search docs..." />
</li>
</ul>
<ul class="nav-site nav-site-external">
<li><a href="https://github.com/facebook/react">GitHub</a></li>
<li><a href="https://github.com/facebook/react/releases">v16.0.0</a></li>
</ul>
</div>
</div>
</div>
<section class="content wrap documentationContent">
<div class="inner-content">
<h1>Implementation Notes</h1>
<div class="subHeader"></div>
<p>This section is a collection of implementation notes for the <a href="/react/contributing/codebase-overview.html#stack-reconciler">stack reconciler</a>.</p>
<p>It is very technical and assumes a strong understanding of React public API as well as how it&#39;s divided into core, renderers, and the reconciler. If you&#39;re not very familiar with the React codebase, read <a href="/react/contributing/codebase-overview.html">the codebase overview</a> first.</p>
<p>It also assumes an understanding of the <a href="/react/blog/2015/12/18/react-components-elements-and-instances.html">differences between React components, their instances, and elements</a>.</p>
<p>The stack reconciler is powering all the React production code today. It is located in <a href="https://github.com/facebook/react/tree/master/src/renderers/shared/stack"><code>src/renderers/shared/stack/reconciler</code></a> and is used by both React DOM and React Native.</p>
<h3>Video: Building React from Scratch</h3>
<p><a href="https://twitter.com/zpao">Paul O&#39;Shannessy</a> gave a talk about <a href="https://www.youtube.com/watch?v=_MAD4Oly9yg">building React from scratch</a> that largely inspired this document.</p>
<p>Both this document and his talk are simplifications of the real codebase so you might get a better understanding by getting familiar with both of them.</p>
<h3>Overview</h3>
<p>The reconciler itself doesn&#39;t have a public API. <a href="/react/contributing/codebase-overview.html#stack-renderers">Renderers</a> like React DOM and React Native use it to efficiently update the user interface according to the React components written by the user.</p>
<h3>Mounting as a Recursive Process</h3>
<p>Let&#39;s consider the first time you mount a component:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">ReactDOM</span><span class="p">.</span><span class="nx">render</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">,</span> <span class="nx">rootEl</span><span class="p">);</span>
</code></pre></div>
<p>React DOM will pass <code>&lt;App /&gt;</code> along to the reconciler. Remember that <code>&lt;App /&gt;</code> is a React element, that is, a description of <em>what</em> to render. You can think about it as a plain object:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">);</span>
<span class="c1">// { type: App, props: {} }</span>
</code></pre></div>
<p>The reconciler will check if <code>App</code> is a class or a function.</p>
<p>If <code>App</code> is a function, the reconciler will call <code>App(props)</code> to get the rendered element.</p>
<p>If <code>App</code> is a class, the reconciler will instantiate an <code>App</code> with <code>new App(props)</code>, call the <code>componentWillMount()</code> lifecycle method, and then will call the <code>render()</code> method to get the rendered element.</p>
<p>Either way, the reconciler will learn the element <code>App</code> &quot;rendered to&quot;.</p>
<p>This process is recursive. <code>App</code> may render to a <code>&lt;Greeting /&gt;</code>, <code>Greeting</code> may render to a <code>&lt;Button /&gt;</code>, and so on. The reconciler will &quot;drill down&quot; through user-defined components recursively as it learns what each component renders to.</p>
<p>You can imagine this process as a pseudocode:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">isClass</span><span class="p">(</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// React.Component subclasses have this flag</span>
<span class="k">return</span> <span class="p">(</span>
<span class="nb">Boolean</span><span class="p">(</span><span class="nx">type</span><span class="p">.</span><span class="nx">prototype</span><span class="p">)</span> <span class="o">&amp;&amp;</span>
<span class="nb">Boolean</span><span class="p">(</span><span class="nx">type</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">isReactComponent</span><span class="p">)</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="c1">// This function takes a React element (e.g. &lt;App /&gt;)</span>
<span class="c1">// and returns a DOM or Native node representing the mounted tree.</span>
<span class="kd">function</span> <span class="nx">mount</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">type</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">type</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
<span class="c1">// We will determine the rendered element</span>
<span class="c1">// by either running the type as function</span>
<span class="c1">// or creating an instance and calling render().</span>
<span class="kd">var</span> <span class="nx">renderedElement</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">isClass</span><span class="p">(</span><span class="nx">type</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// Component class</span>
<span class="kd">var</span> <span class="nx">publicInstance</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">type</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
<span class="c1">// Set the props</span>
<span class="nx">publicInstance</span><span class="p">.</span><span class="nx">props</span> <span class="o">=</span> <span class="nx">props</span><span class="p">;</span>
<span class="c1">// Call the lifecycle if necessary</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">publicInstance</span><span class="p">.</span><span class="nx">componentWillMount</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">publicInstance</span><span class="p">.</span><span class="nx">componentWillMount</span><span class="p">();</span>
<span class="p">}</span>
<span class="c1">// Get the rendered element by calling render()</span>
<span class="nx">renderedElement</span> <span class="o">=</span> <span class="nx">publicInstance</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="c1">// Component function</span>
<span class="nx">renderedElement</span> <span class="o">=</span> <span class="nx">type</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// This process is recursive because a component may</span>
<span class="c1">// return an element with a type of another component.</span>
<span class="k">return</span> <span class="nx">mount</span><span class="p">(</span><span class="nx">renderedElement</span><span class="p">);</span>
<span class="c1">// Note: this implementation is incomplete and recurses infinitely!</span>
<span class="c1">// It only handles elements like &lt;App /&gt; or &lt;Button /&gt;.</span>
<span class="c1">// It doesn&#39;t handle elements like &lt;div /&gt; or &lt;p /&gt; yet.</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">rootEl</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;root&#39;</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">mount</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">);</span>
<span class="nx">rootEl</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>
</code></pre></div>
<blockquote>
<p><strong>Note:</strong></p>
<p>This really <em>is</em> a pseudo-code. It isn&#39;t similar to the real implementation. It will also cause a stack overflow because we haven&#39;t discussed when to stop the recursion.</p>
</blockquote>
<p>Let&#39;s recap a few key ideas in the example above:</p>
<ul>
<li>React elements are plain objects representing the component type (e.g. <code>App</code>) and the props.</li>
<li>User-defined components (e.g. <code>App</code>) can be classes or functions but they all &quot;render to&quot; elements.</li>
<li>&quot;Mounting&quot; is a recursive process that creates a DOM or Native tree given the top-level React element (e.g. <code>&lt;App /&gt;</code>).</li>
</ul>
<h3>Mounting Host Elements</h3>
<p>This process would be useless if we didn&#39;t render something to the screen as a result.</p>
<p>In addition to user-defined (&quot;composite&quot;) components, React elements may also represent platform-specific (&quot;host&quot;) components. For example, <code>Button</code> might return a <code>&lt;div /&gt;</code> from its render method.</p>
<p>If element&#39;s <code>type</code> property is a string, we are dealing with a host element:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">div</span> <span class="o">/&gt;</span><span class="p">);</span>
<span class="c1">// { type: &#39;div&#39;, props: {} }</span>
</code></pre></div>
<p>There is no user-defined code associated with host elements.</p>
<p>When the reconciler encounters a host element, it lets the renderer take care of mounting it. For example, React DOM would create a DOM node.</p>
<p>If the host element has children, the reconciler recursively mounts them following the same algorithm as above. It doesn&#39;t matter whether children are host (like <code>&lt;div&gt;&lt;hr /&gt;&lt;/div&gt;</code>), composite (like <code>&lt;div&gt;&lt;Button /&gt;&lt;/div&gt;</code>), or both.</p>
<p>The DOM nodes produced by the child components will be appended to the parent DOM node, and recursively, the complete DOM structure will be assembled.</p>
<blockquote>
<p><strong>Note:</strong></p>
<p>The reconciler itself is not tied to the DOM. The exact result of mounting (sometimes called &quot;mount image&quot; in the source code) depends on the renderer, and can be a DOM node (React DOM), a string (React DOM Server), or a number representing a native view (React Native).</p>
</blockquote>
<p>If we were to extend the code to handle host elements, it would look like this:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">isClass</span><span class="p">(</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// React.Component subclasses have this flag</span>
<span class="k">return</span> <span class="p">(</span>
<span class="nb">Boolean</span><span class="p">(</span><span class="nx">type</span><span class="p">.</span><span class="nx">prototype</span><span class="p">)</span> <span class="o">&amp;&amp;</span>
<span class="nb">Boolean</span><span class="p">(</span><span class="nx">type</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">isReactComponent</span><span class="p">)</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="c1">// This function only handles elements with a composite type.</span>
<span class="c1">// For example, it handles &lt;App /&gt; and &lt;Button /&gt;, but not a &lt;div /&gt;.</span>
<span class="kd">function</span> <span class="nx">mountComposite</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">type</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">type</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">renderedElement</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">isClass</span><span class="p">(</span><span class="nx">type</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// Component class</span>
<span class="kd">var</span> <span class="nx">publicInstance</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">type</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
<span class="c1">// Set the props</span>
<span class="nx">publicInstance</span><span class="p">.</span><span class="nx">props</span> <span class="o">=</span> <span class="nx">props</span><span class="p">;</span>
<span class="c1">// Call the lifecycle if necessary</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">publicInstance</span><span class="p">.</span><span class="nx">componentWillMount</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">publicInstance</span><span class="p">.</span><span class="nx">componentWillMount</span><span class="p">();</span>
<span class="p">}</span>
<span class="nx">renderedElement</span> <span class="o">=</span> <span class="nx">publicInstance</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">type</span> <span class="o">===</span> <span class="s1">&#39;function&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Component function</span>
<span class="nx">renderedElement</span> <span class="o">=</span> <span class="nx">type</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// This is recursive but we&#39;ll eventually reach the bottom of recursion when</span>
<span class="c1">// the element is host (e.g. &lt;div /&gt;) rather than composite (e.g. &lt;App /&gt;):</span>
<span class="k">return</span> <span class="nx">mount</span><span class="p">(</span><span class="nx">renderedElement</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// This function only handles elements with a host type.</span>
<span class="c1">// For example, it handles &lt;div /&gt; and &lt;p /&gt; but not an &lt;App /&gt;.</span>
<span class="kd">function</span> <span class="nx">mountHost</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">type</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">type</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">children</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">children</span> <span class="o">||</span> <span class="p">[];</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">children</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">children</span> <span class="o">=</span> <span class="p">[</span><span class="nx">children</span><span class="p">];</span>
<span class="p">}</span>
<span class="nx">children</span> <span class="o">=</span> <span class="nx">children</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nb">Boolean</span><span class="p">);</span>
<span class="c1">// This block of code shouldn&#39;t be in the reconciler.</span>
<span class="c1">// Different renderers might initialize nodes differently.</span>
<span class="c1">// For example, React Native would create iOS or Android views.</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="nx">type</span><span class="p">);</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">props</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">propName</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">propName</span> <span class="o">!==</span> <span class="s1">&#39;children&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="nx">propName</span><span class="p">,</span> <span class="nx">props</span><span class="p">[</span><span class="nx">propName</span><span class="p">]);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="c1">// Mount the children</span>
<span class="nx">children</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">childElement</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="c1">// Children may be host (e.g. &lt;div /&gt;) or composite (e.g. &lt;Button /&gt;).</span>
<span class="c1">// We will also mount them recursively:</span>
<span class="kd">var</span> <span class="nx">childNode</span> <span class="o">=</span> <span class="nx">mount</span><span class="p">(</span><span class="nx">childElement</span><span class="p">);</span>
<span class="c1">// This line of code is also renderer-specific.</span>
<span class="c1">// It would be different depending on the renderer:</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">childNode</span><span class="p">);</span>
<span class="p">});</span>
<span class="c1">// Return the DOM node as mount result.</span>
<span class="c1">// This is where the recursion ends.</span>
<span class="k">return</span> <span class="nx">node</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">mount</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">type</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">type</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">type</span> <span class="o">===</span> <span class="s1">&#39;function&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// User-defined components</span>
<span class="k">return</span> <span class="nx">mountComposite</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">type</span> <span class="o">===</span> <span class="s1">&#39;string&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Platform-specific components</span>
<span class="k">return</span> <span class="nx">mountHost</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">rootEl</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;root&#39;</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">mount</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">);</span>
<span class="nx">rootEl</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>
</code></pre></div>
<p>This is working but still far from how the reconciler is really implemented. The key missing ingredient is support for updates.</p>
<h3>Introducing Internal Instances</h3>
<p>The key feature of React is that you can re-render everything, and it won&#39;t recreate the DOM or reset the state:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="nx">ReactDOM</span><span class="p">.</span><span class="nx">render</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">,</span> <span class="nx">rootEl</span><span class="p">);</span>
<span class="c1">// Should reuse the existing DOM:</span>
<span class="nx">ReactDOM</span><span class="p">.</span><span class="nx">render</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">,</span> <span class="nx">rootEl</span><span class="p">);</span>
</code></pre></div>
<p>However, our implementation above only knows how to mount the initial tree. It can&#39;t perform updates on it because it doesn&#39;t store all the necessary information, such as all the <code>publicInstance</code>s, or which DOM <code>node</code>s correspond to which components.</p>
<p>The stack reconciler codebase solves this by making the <code>mount()</code> function a method and putting it on a class. There are drawbacks to this approach, and we are going in the opposite direction in the <a href="/react/contributing/codebase-overview.html#fiber-reconciler">ongoing rewrite of the reconciler</a>. Nevertheless this is how it works now.</p>
<p>Instead of separate <code>mountHost</code> and <code>mountComposite</code> functions, we will create two classes: <code>DOMComponent</code> and <code>CompositeComponent</code>.</p>
<p>Both classes have a constructor accepting the <code>element</code>, as well as a <code>mount()</code> method returning the mounted node. We will replace a top-level <code>mount()</code> function with a factory that instantiates the correct class:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">instantiateComponent</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">type</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">type</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">type</span> <span class="o">===</span> <span class="s1">&#39;function&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// User-defined components</span>
<span class="k">return</span> <span class="k">new</span> <span class="nx">CompositeComponent</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">type</span> <span class="o">===</span> <span class="s1">&#39;string&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Platform-specific components</span>
<span class="k">return</span> <span class="k">new</span> <span class="nx">DOMComponent</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>First, let&#39;s consider the implementation of <code>CompositeComponent</code>:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">class</span> <span class="nx">CompositeComponent</span> <span class="p">{</span>
<span class="nx">constructor</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">currentElement</span> <span class="o">=</span> <span class="nx">element</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">renderedComponent</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">publicInstance</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">getPublicInstance</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// For composite components, expose the class instance.</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">publicInstance</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">mount</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">element</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">currentElement</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">type</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">type</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">publicInstance</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">renderedElement</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">isClass</span><span class="p">(</span><span class="nx">type</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// Component class</span>
<span class="nx">publicInstance</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">type</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
<span class="c1">// Set the props</span>
<span class="nx">publicInstance</span><span class="p">.</span><span class="nx">props</span> <span class="o">=</span> <span class="nx">props</span><span class="p">;</span>
<span class="c1">// Call the lifecycle if necessary</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">publicInstance</span><span class="p">.</span><span class="nx">componentWillMount</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">publicInstance</span><span class="p">.</span><span class="nx">componentWillMount</span><span class="p">();</span>
<span class="p">}</span>
<span class="nx">renderedElement</span> <span class="o">=</span> <span class="nx">publicInstance</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">type</span> <span class="o">===</span> <span class="s1">&#39;function&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Component function</span>
<span class="nx">publicInstance</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="nx">renderedElement</span> <span class="o">=</span> <span class="nx">type</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Save the public instance</span>
<span class="k">this</span><span class="p">.</span><span class="nx">publicInstance</span> <span class="o">=</span> <span class="nx">publicInstance</span><span class="p">;</span>
<span class="c1">// Instantiate the child internal instance according to the element.</span>
<span class="c1">// It would be a DOMComponent for &lt;div /&gt; or &lt;p /&gt;,</span>
<span class="c1">// and a CompositeComponent for &lt;App /&gt; or &lt;Button /&gt;:</span>
<span class="kd">var</span> <span class="nx">renderedComponent</span> <span class="o">=</span> <span class="nx">instantiateComponent</span><span class="p">(</span><span class="nx">renderedElement</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">renderedComponent</span> <span class="o">=</span> <span class="nx">renderedComponent</span><span class="p">;</span>
<span class="c1">// Mount the rendered output</span>
<span class="k">return</span> <span class="nx">renderedComponent</span><span class="p">.</span><span class="nx">mount</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>This is not much different from our previous <code>mountComposite()</code> implementation, but now we can save some information, such as <code>this.currentElement</code>, <code>this.renderedComponent</code>, and <code>this.publicInstance</code>, for use during updates.</p>
<p>Note that an instance of <code>CompositeComponent</code> is not the same thing as an instance of the user-supplied <code>element.type</code>. <code>CompositeComponent</code> is an implementation detail of our reconciler, and is never exposed to the user. The user-defined class is the one we read from <code>element.type</code>, and <code>CompositeComponent</code> creates an instance of it.</p>
<p>To avoid the confusion, we will call instances of <code>CompositeComponent</code> and <code>DOMComponent</code> &quot;internal instances&quot;. They exist so we can associate some long-lived data with them. Only the renderer and the reconciler are aware that they exist.</p>
<p>In contrast, we call an instance of the user-defined class a &quot;public instance&quot;. The public instance is what you see as <code>this</code> in the <code>render()</code> and other methods of your custom components.</p>
<p>The <code>mountHost()</code> function, refactored to be a <code>mount()</code> method on <code>DOMComponent</code> class, also looks familiar:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">class</span> <span class="nx">DOMComponent</span> <span class="p">{</span>
<span class="nx">constructor</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">currentElement</span> <span class="o">=</span> <span class="nx">element</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">renderedChildren</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">this</span><span class="p">.</span><span class="nx">node</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">getPublicInstance</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// For DOM components, only expose the DOM node.</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">node</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">mount</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">element</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">currentElement</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">type</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">type</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">children</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">children</span> <span class="o">||</span> <span class="p">[];</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">children</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">children</span> <span class="o">=</span> <span class="p">[</span><span class="nx">children</span><span class="p">];</span>
<span class="p">}</span>
<span class="c1">// Create and save the node</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="nx">type</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">node</span> <span class="o">=</span> <span class="nx">node</span><span class="p">;</span>
<span class="c1">// Set the attributes</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">props</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">propName</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">propName</span> <span class="o">!==</span> <span class="s1">&#39;children&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="nx">propName</span><span class="p">,</span> <span class="nx">props</span><span class="p">[</span><span class="nx">propName</span><span class="p">]);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="c1">// Create and save the contained children.</span>
<span class="c1">// Each of them can be a DOMComponent or a CompositeComponent,</span>
<span class="c1">// depending on whether the element type is a string or a function.</span>
<span class="kd">var</span> <span class="nx">renderedChildren</span> <span class="o">=</span> <span class="nx">children</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">instantiateComponent</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">renderedChildren</span> <span class="o">=</span> <span class="nx">renderedChildren</span><span class="p">;</span>
<span class="c1">// Collect DOM nodes they return on mount</span>
<span class="kd">var</span> <span class="nx">childNodes</span> <span class="o">=</span> <span class="nx">renderedChildren</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">child</span> <span class="o">=&gt;</span> <span class="nx">child</span><span class="p">.</span><span class="nx">mount</span><span class="p">());</span>
<span class="nx">childNodes</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">childNode</span> <span class="o">=&gt;</span> <span class="nx">node</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">childNode</span><span class="p">));</span>
<span class="c1">// Return the DOM node as mount result</span>
<span class="k">return</span> <span class="nx">node</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>The main difference after refactoring from <code>mountHost()</code> is that we now keep <code>this.node</code> and <code>this.renderedChildren</code> associated with the internal DOM component instance. We will also use them for applying non-destructive updates in the future.</p>
<p>As a result, each internal instance, composite or host, now points to its child internal instances. To help visualize this, if a functional <code>&lt;App&gt;</code> component renders a <code>&lt;Button&gt;</code> class component, and <code>Button</code> class renders a <code>&lt;div&gt;</code>, the internal instance tree would look like this:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="p">[</span><span class="nx">object</span> <span class="nx">CompositeComponent</span><span class="p">]</span> <span class="p">{</span>
<span class="nx">currentElement</span><span class="o">:</span> <span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">,</span>
<span class="nx">publicInstance</span><span class="o">:</span> <span class="kc">null</span><span class="p">,</span>
<span class="nx">renderedComponent</span><span class="o">:</span> <span class="p">[</span><span class="nx">object</span> <span class="nx">CompositeComponent</span><span class="p">]</span> <span class="p">{</span>
<span class="nx">currentElement</span><span class="o">:</span> <span class="o">&lt;</span><span class="nx">Button</span> <span class="o">/&gt;</span><span class="p">,</span>
<span class="nx">publicInstance</span><span class="o">:</span> <span class="p">[</span><span class="nx">object</span> <span class="nx">Button</span><span class="p">],</span>
<span class="nx">renderedComponent</span><span class="o">:</span> <span class="p">[</span><span class="nx">object</span> <span class="nx">DOMComponent</span><span class="p">]</span> <span class="p">{</span>
<span class="nx">currentElement</span><span class="o">:</span> <span class="o">&lt;</span><span class="nx">div</span> <span class="o">/&gt;</span><span class="p">,</span>
<span class="nx">node</span><span class="o">:</span> <span class="p">[</span><span class="nx">object</span> <span class="nx">HTMLDivElement</span><span class="p">],</span>
<span class="nx">renderedChildren</span><span class="o">:</span> <span class="p">[]</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>In the DOM you would only see the <code>&lt;div&gt;</code>. However the internal instance tree contains both composite and host internal instances.</p>
<p>The composite internal instances need to store:</p>
<ul>
<li>The current element.</li>
<li>The public instance if element type is a class.</li>
<li>The single rendered internal instance. It can be either a <code>DOMComponent</code> or a <code>CompositeComponent</code>.</li>
</ul>
<p>The host internal instances need to store:</p>
<ul>
<li>The current element.</li>
<li>The DOM node.</li>
<li>All the child internal instances. Each of them can be either a <code>DOMComponent</code> or a <code>CompositeComponent</code>.</li>
</ul>
<p>If you&#39;re struggling to imagine how an internal instance tree is structured in more complex applications, <a href="https://github.com/facebook/react-devtools">React DevTools</a> can give you a close approximation, as it highlights host instances with grey, and composite instances with purple:</p>
<p><img src="/react/img/docs/implementation-notes-tree.png" width="500" style="max-width: 100%" alt="React DevTools tree" /></p>
<p>To complete this refactoring, we will introduce a function that mounts a complete tree into a container node, just like <code>ReactDOM.render()</code>. It returns a public instance, also like <code>ReactDOM.render()</code>:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">mountTree</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span> <span class="nx">containerNode</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Create the top-level internal instance</span>
<span class="kd">var</span> <span class="nx">rootComponent</span> <span class="o">=</span> <span class="nx">instantiateComponent</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
<span class="c1">// Mount the top-level component into the container</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">rootComponent</span><span class="p">.</span><span class="nx">mount</span><span class="p">();</span>
<span class="nx">containerNode</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>
<span class="c1">// Return the public instance it provides</span>
<span class="kd">var</span> <span class="nx">publicInstance</span> <span class="o">=</span> <span class="nx">rootComponent</span><span class="p">.</span><span class="nx">getPublicInstance</span><span class="p">();</span>
<span class="k">return</span> <span class="nx">publicInstance</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">rootEl</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;root&#39;</span><span class="p">);</span>
<span class="nx">mountTree</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">,</span> <span class="nx">rootEl</span><span class="p">);</span>
</code></pre></div>
<h3>Unmounting</h3>
<p>Now that we have internal instances that hold onto their children and the DOM nodes, we can implement unmounting. For a composite component, unmounting calls a lifecycle hook and recurses.</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">class</span> <span class="nx">CompositeComponent</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">unmount</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Call the lifecycle hook if necessary</span>
<span class="kd">var</span> <span class="nx">publicInstance</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">publicInstance</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">publicInstance</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">publicInstance</span><span class="p">.</span><span class="nx">componentWillUnmount</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">publicInstance</span><span class="p">.</span><span class="nx">componentWillUnmount</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Unmount the single rendered component</span>
<span class="kd">var</span> <span class="nx">renderedComponent</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">renderedComponent</span><span class="p">;</span>
<span class="nx">renderedComponent</span><span class="p">.</span><span class="nx">unmount</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>For <code>DOMComponent</code>, unmounting tells each child to unmount:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">class</span> <span class="nx">DOMComponent</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">unmount</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Unmount all the children</span>
<span class="kd">var</span> <span class="nx">renderedChildren</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">renderedChildren</span><span class="p">;</span>
<span class="nx">renderedChildren</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">child</span> <span class="o">=&gt;</span> <span class="nx">child</span><span class="p">.</span><span class="nx">unmount</span><span class="p">());</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>In practice, unmounting DOM components also removes the event listeners and clears some caches, but we will skip those details.</p>
<p>We can now add a new top-level function called <code>unmountTree(containerNode)</code> that is similar to <code>ReactDOM.unmountComponentAtNode()</code>:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">unmountTree</span><span class="p">(</span><span class="nx">containerNode</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Read the internal instance from a DOM node:</span>
<span class="c1">// (This doesn&#39;t work yet, we will need to change mountTree() to store it.)</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">containerNode</span><span class="p">.</span><span class="nx">firstChild</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">rootComponent</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">_internalInstance</span><span class="p">;</span>
<span class="c1">// Unmount the tree and clear the container</span>
<span class="nx">rootComponent</span><span class="p">.</span><span class="nx">unmount</span><span class="p">();</span>
<span class="nx">containerNode</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>In order for this to work, we need to read an internal root instance from a DOM node. We will modify <code>mountTree()</code> to add the <code>_internalInstance</code> property to the root DOM node. We will also teach <code>mountTree()</code> to destroy any existing tree so it can be called multiple times:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">mountTree</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span> <span class="nx">containerNode</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Destroy any existing tree</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">containerNode</span><span class="p">.</span><span class="nx">firstChild</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">unmountTree</span><span class="p">(</span><span class="nx">containerNode</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Create the top-level internal instance</span>
<span class="kd">var</span> <span class="nx">rootComponent</span> <span class="o">=</span> <span class="nx">instantiateComponent</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
<span class="c1">// Mount the top-level component into the container</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">rootComponent</span><span class="p">.</span><span class="nx">mount</span><span class="p">();</span>
<span class="nx">containerNode</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>
<span class="c1">// Save a reference to the internal instance</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">_internalInstance</span> <span class="o">=</span> <span class="nx">rootComponent</span><span class="p">;</span>
<span class="c1">// Return the public instance it provides</span>
<span class="kd">var</span> <span class="nx">publicInstance</span> <span class="o">=</span> <span class="nx">rootComponent</span><span class="p">.</span><span class="nx">getPublicInstance</span><span class="p">();</span>
<span class="k">return</span> <span class="nx">publicInstance</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
<p>Now, running <code>unmountTree()</code>, or running <code>mountTree()</code> repeatedly, removes the old tree and runs the <code>componentWillUnmount()</code> lifecycle hook on components.</p>
<h3>Updating</h3>
<p>In the previous section, we implemented unmounting. However React wouldn&#39;t be very useful if each prop change unmounted and mounted the whole tree. The goal of the reconciler is to reuse existing instances where possible to preserve the DOM and the state:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">rootEl</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;root&#39;</span><span class="p">);</span>
<span class="nx">mountTree</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">,</span> <span class="nx">rootEl</span><span class="p">);</span>
<span class="c1">// Should reuse the existing DOM:</span>
<span class="nx">mountTree</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">,</span> <span class="nx">rootEl</span><span class="p">);</span>
</code></pre></div>
<p>We will extend our internal instance contract with one more method. In addition to <code>mount()</code> and <code>unmount()</code>, both <code>DOMComponent</code> and <code>CompositeComponent</code> will implement a new method called <code>receive(nextElement)</code>:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">class</span> <span class="nx">CompositeComponent</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">receive</span><span class="p">(</span><span class="nx">nextElement</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kr">class</span> <span class="nx">DOMComponent</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">receive</span><span class="p">(</span><span class="nx">nextElement</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>Its job is to do whatever is necessary to bring the component (and any of its children) up to date with the description provided by the <code>nextElement</code>.</p>
<p>This is the part that is often described as &quot;virtual DOM diffing&quot; although what really happens is that we walk the internal tree recursively and let each internal instance receive an update.</p>
<h3>Updating Composite Components</h3>
<p>When a composite component receives a new element, we run the <code>componentWillUpdate()</code> lifecycle hook.</p>
<p>Then we re-render the component with the new props, and get the next rendered element:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">class</span> <span class="nx">CompositeComponent</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">receive</span><span class="p">(</span><span class="nx">nextElement</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">prevProps</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">currentElement</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">publicInstance</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">publicInstance</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">prevRenderedComponent</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">renderedComponent</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">prevRenderedElement</span> <span class="o">=</span> <span class="nx">prevRenderedComponent</span><span class="p">.</span><span class="nx">currentElement</span><span class="p">;</span>
<span class="c1">// Update *own* element</span>
<span class="k">this</span><span class="p">.</span><span class="nx">currentElement</span> <span class="o">=</span> <span class="nx">nextElement</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">type</span> <span class="o">=</span> <span class="nx">nextElement</span><span class="p">.</span><span class="nx">type</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">nextProps</span> <span class="o">=</span> <span class="nx">nextElement</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
<span class="c1">// Figure out what the next render() output is</span>
<span class="kd">var</span> <span class="nx">nextRenderedElement</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">isClass</span><span class="p">(</span><span class="nx">type</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// Component class</span>
<span class="c1">// Call the lifecycle if necessary</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">publicInstance</span><span class="p">.</span><span class="nx">componentWillUpdate</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">publicInstance</span><span class="p">.</span><span class="nx">componentWillUpdate</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Update the props</span>
<span class="nx">publicInstance</span><span class="p">.</span><span class="nx">props</span> <span class="o">=</span> <span class="nx">nextProps</span><span class="p">;</span>
<span class="c1">// Re-render</span>
<span class="nx">nextRenderedElement</span> <span class="o">=</span> <span class="nx">publicInstance</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">type</span> <span class="o">===</span> <span class="s1">&#39;function&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Component function</span>
<span class="nx">nextRenderedElement</span> <span class="o">=</span> <span class="nx">type</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// ...</span>
</code></pre></div>
<p>Next, we can look at the rendered element&#39;s <code>type</code>. If the <code>type</code> has not changed since the last render, the component below can also be updated in place.</p>
<p>For example, if it returned <code>&lt;Button color=&quot;red&quot; /&gt;</code> the first time, and <code>&lt;Button color=&quot;blue&quot; /&gt;</code> the second time, we can just tell the corresponding internal instance to <code>receive()</code> the next element:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"> <span class="c1">// ...</span>
<span class="c1">// If the rendered element type has not changed,</span>
<span class="c1">// reuse the existing component instance and exit.</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">prevRenderedElement</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="nx">nextRenderedElement</span><span class="p">.</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">prevRenderedComponent</span><span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="nx">nextRenderedElement</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// ...</span>
</code></pre></div>
<p>However, if the next rendered element has a different <code>type</code> than the previously rendered element, we can&#39;t update the internal instance. A <code>&lt;button&gt;</code> can&#39;t &quot;become&quot; an <code>&lt;input&gt;</code>.</p>
<p>Instead, we have to unmount the existing internal instance and mount the new one corresponding to the rendered element type. For example, this is what happens when a component that previously rendered a <code>&lt;button /&gt;</code> renders an <code>&lt;input /&gt;</code>:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"> <span class="c1">// ...</span>
<span class="c1">// If we reached this point, we need to unmount the previously</span>
<span class="c1">// mounted component, mount the new one, and swap their nodes.</span>
<span class="c1">// Find the old node because it will need to be replaced</span>
<span class="kd">var</span> <span class="nx">prevNode</span> <span class="o">=</span> <span class="nx">prevRenderedComponent</span><span class="p">.</span><span class="nx">getHostNode</span><span class="p">();</span>
<span class="c1">// Unmount the old child and mount a new child</span>
<span class="nx">prevRenderedComponent</span><span class="p">.</span><span class="nx">unmount</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">nextRenderedComponent</span> <span class="o">=</span> <span class="nx">instantiateComponent</span><span class="p">(</span><span class="nx">nextRenderedElement</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">nextNode</span> <span class="o">=</span> <span class="nx">nextRenderedComponent</span><span class="p">.</span><span class="nx">mount</span><span class="p">();</span>
<span class="c1">// Replace the reference to the child</span>
<span class="k">this</span><span class="p">.</span><span class="nx">renderedComponent</span> <span class="o">=</span> <span class="nx">nextRenderedComponent</span><span class="p">;</span>
<span class="c1">// Replace the old node with the new one</span>
<span class="c1">// Note: this is renderer-specific code and</span>
<span class="c1">// ideally should live outside of CompositeComponent:</span>
<span class="nx">prevNode</span><span class="p">.</span><span class="nx">parentNode</span><span class="p">.</span><span class="nx">replaceChild</span><span class="p">(</span><span class="nx">nextNode</span><span class="p">,</span> <span class="nx">prevNode</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>To sum this up, when a composite component receives a new element, it may either delegate the update to its rendered internal instance, or unmount it and mount a new one in its place.</p>
<p>There is another condition under which a component will re-mount rather than receive an element, and that is when the element&#39;s <code>key</code> has changed. We don&#39;t discuss <code>key</code> handling in this document because it adds more complexity to an already complex tutorial.</p>
<p>Note that we needed to add a method called <code>getHostNode()</code> to the internal instance contract so that it&#39;s possible to locate the platform-specific node and replace it during the update. Its implementation is straightforward for both classes:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">class</span> <span class="nx">CompositeComponent</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">getHostNode</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Ask the rendered component to provide it.</span>
<span class="c1">// This will recursively drill down any composites.</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">renderedComponent</span><span class="p">.</span><span class="nx">getHostNode</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="kr">class</span> <span class="nx">DOMComponent</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">getHostNode</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">node</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<h3>Updating Host Components</h3>
<p>Host component implementations, such as <code>DOMComponent</code>, update differently. When they receive an element, they need to update the underlying platform-specific view. In case of React DOM, this means updating the DOM attributes:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kr">class</span> <span class="nx">DOMComponent</span> <span class="p">{</span>
<span class="c1">// ...</span>
<span class="nx">receive</span><span class="p">(</span><span class="nx">nextElement</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">node</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">prevElement</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">currentElement</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">prevProps</span> <span class="o">=</span> <span class="nx">prevElement</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">nextProps</span> <span class="o">=</span> <span class="nx">nextElement</span><span class="p">.</span><span class="nx">props</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">currentElement</span> <span class="o">=</span> <span class="nx">nextElement</span><span class="p">;</span>
<span class="c1">// Remove old attributes.</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">prevProps</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">propName</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">propName</span> <span class="o">!==</span> <span class="s1">&#39;children&#39;</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nx">nextProps</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">propName</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">removeAttribute</span><span class="p">(</span><span class="nx">propName</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="c1">// Set next attributes.</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">nextProps</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">propName</span> <span class="o">=&gt;</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">propName</span> <span class="o">!==</span> <span class="s1">&#39;children&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">node</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="nx">propName</span><span class="p">,</span> <span class="nx">nextProps</span><span class="p">[</span><span class="nx">propName</span><span class="p">]);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="c1">// ...</span>
</code></pre></div>
<p>Then, host components need to update their children. Unlike composite components, they might contain more than a single child.</p>
<p>In this simplified example, we use an array of internal instances and iterate over it, either updating or replacing the internal instances depending on whether the received <code>type</code> matches their previous <code>type</code>. The real reconciler also takes element&#39;s <code>key</code> in the account and track moves in addition to insertions and deletions, but we will omit this logic.</p>
<p>We collect DOM operations on children in a list so we can execute them in batch:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"> <span class="c1">// ...</span>
<span class="c1">// These are arrays of React elements:</span>
<span class="kd">var</span> <span class="nx">prevChildren</span> <span class="o">=</span> <span class="nx">prevProps</span><span class="p">.</span><span class="nx">children</span> <span class="o">||</span> <span class="p">[];</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">prevChildren</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">prevChildren</span> <span class="o">=</span> <span class="p">[</span><span class="nx">prevChildren</span><span class="p">];</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">nextChildren</span> <span class="o">=</span> <span class="nx">nextProps</span><span class="p">.</span><span class="nx">children</span> <span class="o">||</span> <span class="p">[];</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">nextChildren</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">nextChildren</span> <span class="o">=</span> <span class="p">[</span><span class="nx">nextChildren</span><span class="p">];</span>
<span class="p">}</span>
<span class="c1">// These are arrays of internal instances:</span>
<span class="kd">var</span> <span class="nx">prevRenderedChildren</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">renderedChildren</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">nextRenderedChildren</span> <span class="o">=</span> <span class="p">[];</span>
<span class="c1">// As we iterate over children, we will add operations to the array.</span>
<span class="kd">var</span> <span class="nx">operationQueue</span> <span class="o">=</span> <span class="p">[];</span>
<span class="c1">// Note: the section below is extremely simplified!</span>
<span class="c1">// It doesn&#39;t handle reorders, children with holes, or keys.</span>
<span class="c1">// It only exists to illustrate the overall flow, not the specifics.</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">nextChildren</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Try to get an existing internal instance for this child</span>
<span class="kd">var</span> <span class="nx">prevChild</span> <span class="o">=</span> <span class="nx">prevRenderedChildren</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>
<span class="c1">// If there is no internal instance under this index,</span>
<span class="c1">// a child has been appended to the end. Create a new</span>
<span class="c1">// internal instance, mount it, and use its node.</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">prevChild</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">nextChild</span> <span class="o">=</span> <span class="nx">instantiateComponent</span><span class="p">(</span><span class="nx">nextChildren</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">nextChild</span><span class="p">.</span><span class="nx">mount</span><span class="p">();</span>
<span class="c1">// Record that we need to append a node</span>
<span class="nx">operationQueue</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span> <span class="s1">&#39;ADD&#39;</span><span class="p">,</span> <span class="nx">node</span><span class="p">});</span>
<span class="nx">nextRenderedChildren</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">nextChild</span><span class="p">);</span>
<span class="k">continue</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// We can only update the instance if its element&#39;s type matches.</span>
<span class="c1">// For example, &lt;Button size=&quot;small&quot; /&gt; can be updated to</span>
<span class="c1">// &lt;Button size=&quot;large&quot; /&gt; but not to an &lt;App /&gt;.</span>
<span class="kd">var</span> <span class="nx">canUpdate</span> <span class="o">=</span> <span class="nx">prevChildren</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">type</span> <span class="o">===</span> <span class="nx">nextChildren</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">type</span><span class="p">;</span>
<span class="c1">// If we can&#39;t update an existing instance, we have to unmount it</span>
<span class="c1">// and mount a new one instead of it.</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">canUpdate</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">prevNode</span> <span class="o">=</span> <span class="nx">prevChild</span><span class="p">.</span><span class="nx">node</span><span class="p">;</span>
<span class="nx">prevChild</span><span class="p">.</span><span class="nx">unmount</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">nextChild</span> <span class="o">=</span> <span class="nx">instantiateComponent</span><span class="p">(</span><span class="nx">nextChildren</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span>
<span class="kd">var</span> <span class="nx">nextNode</span> <span class="o">=</span> <span class="nx">nextChild</span><span class="p">.</span><span class="nx">mount</span><span class="p">();</span>
<span class="c1">// Record that we need to swap the nodes</span>
<span class="nx">operationQueue</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span> <span class="s1">&#39;REPLACE&#39;</span><span class="p">,</span> <span class="nx">prevNode</span><span class="p">,</span> <span class="nx">nextNode</span><span class="p">});</span>
<span class="nx">nextRenderedChildren</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">nextChild</span><span class="p">);</span>
<span class="k">continue</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// If we can update an existing internal instance,</span>
<span class="c1">// just let it receive the next element and handle its own update.</span>
<span class="nx">prevChild</span><span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="nx">nextChildren</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span>
<span class="nx">nextRenderedChildren</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">prevChild</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Finally, unmount any children that don&#39;t exist:</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="nx">nextChildren</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">j</span> <span class="o">&lt;</span> <span class="nx">prevChildren</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">prevChild</span> <span class="o">=</span> <span class="nx">prevRenderedChildren</span><span class="p">[</span><span class="nx">j</span><span class="p">];</span>
<span class="kd">var</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">prevChild</span><span class="p">.</span><span class="nx">node</span><span class="p">;</span>
<span class="nx">prevChild</span><span class="p">.</span><span class="nx">unmount</span><span class="p">();</span>
<span class="c1">// Record that we need to remove the node</span>
<span class="nx">operationQueue</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span><span class="nx">type</span><span class="o">:</span> <span class="s1">&#39;REMOVE&#39;</span><span class="p">,</span> <span class="nx">node</span><span class="p">});</span>
<span class="p">}</span>
<span class="c1">// Point the list of rendered children to the updated version.</span>
<span class="k">this</span><span class="p">.</span><span class="nx">renderedChildren</span> <span class="o">=</span> <span class="nx">nextRenderedChildren</span><span class="p">;</span>
<span class="c1">// ...</span>
</code></pre></div>
<p>As the last step, we execute the DOM operations. Again, the real reconciler code is more complex because it also handles moves:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"> <span class="c1">// ...</span>
<span class="c1">// Process the operation queue.</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">operationQueue</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">operation</span> <span class="o">=</span> <span class="nx">operationQueue</span><span class="p">.</span><span class="nx">shift</span><span class="p">();</span>
<span class="k">switch</span> <span class="p">(</span><span class="nx">operation</span><span class="p">.</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="s1">&#39;ADD&#39;</span><span class="o">:</span>
<span class="k">this</span><span class="p">.</span><span class="nx">node</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">operation</span><span class="p">.</span><span class="nx">node</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="s1">&#39;REPLACE&#39;</span><span class="o">:</span>
<span class="k">this</span><span class="p">.</span><span class="nx">node</span><span class="p">.</span><span class="nx">replaceChild</span><span class="p">(</span><span class="nx">operation</span><span class="p">.</span><span class="nx">nextNode</span><span class="p">,</span> <span class="nx">operation</span><span class="p">.</span><span class="nx">prevNode</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="s1">&#39;REMOVE&#39;</span><span class="o">:</span>
<span class="k">this</span><span class="p">.</span><span class="nx">node</span><span class="p">.</span><span class="nx">removeChild</span><span class="p">(</span><span class="nx">operation</span><span class="p">.</span><span class="nx">node</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
<p>And that is it for updating host components.</p>
<h3>Top-Level Updates</h3>
<p>Now that both <code>CompositeComponent</code> and <code>DOMComponent</code> implement the <code>receive(nextElement)</code> method, we can change the top-level <code>mountTree()</code> function to use it when the element <code>type</code> is the same as it was the last time:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">function</span> <span class="nx">mountTree</span><span class="p">(</span><span class="nx">element</span><span class="p">,</span> <span class="nx">containerNode</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Check for an existing tree</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">containerNode</span><span class="p">.</span><span class="nx">firstChild</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">prevNode</span> <span class="o">=</span> <span class="nx">containerNode</span><span class="p">.</span><span class="nx">firstChild</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">prevRootComponent</span> <span class="o">=</span> <span class="nx">prevNode</span><span class="p">.</span><span class="nx">_internalInstance</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">prevElement</span> <span class="o">=</span> <span class="nx">prevRootComponent</span><span class="p">.</span><span class="nx">currentElement</span><span class="p">;</span>
<span class="c1">// If we can, reuse the existing root component</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">prevElement</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="nx">element</span><span class="p">.</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">prevRootComponent</span><span class="p">.</span><span class="nx">receive</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
<span class="k">return</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Otherwise, unmount the existing tree</span>
<span class="nx">unmountTree</span><span class="p">(</span><span class="nx">containerNode</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div>
<p>Now calling <code>mountTree()</code> two times with the same type isn&#39;t destructive:</p>
<div class="highlight"><pre><code class="language-js" data-lang="js"><span class="kd">var</span> <span class="nx">rootEl</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">&#39;root&#39;</span><span class="p">);</span>
<span class="nx">mountTree</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">,</span> <span class="nx">rootEl</span><span class="p">);</span>
<span class="c1">// Reuses the existing DOM:</span>
<span class="nx">mountTree</span><span class="p">(</span><span class="o">&lt;</span><span class="nx">App</span> <span class="o">/&gt;</span><span class="p">,</span> <span class="nx">rootEl</span><span class="p">);</span>
</code></pre></div>
<p>These are the basics of how React works internally.</p>
<h3>What We Left Out</h3>
<p>This document is simplified compared to the real codebase. There are a few important aspects we didn&#39;t address:</p>
<ul>
<li><p>Components can render <code>null</code>, and the reconciler can handle &quot;empty slots&quot; in arrays and rendered output.</p></li>
<li><p>The reconciler also reads <code>key</code> from the elements, and uses it to establish which internal instance corresponds to which element in an array. A bulk of complexity in the actual React implementation is related to that.</p></li>
<li><p>In addition to composite and host internal instance classes, there are also classes for &quot;text&quot; and &quot;empty&quot; components. They represent text nodes and the &quot;empty slots&quot; you get by rendering <code>null</code>.</p></li>
<li><p>Renderers use <a href="/react/contributing/codebase-overview.html#dynamic-injection">injection</a> to pass the host internal class to the reconciler. For example, React DOM tells the reconciler to use <code>ReactDOMComponent</code> as the host internal instance implementation.</p></li>
<li><p>The logic for updating the list of children is extracted into a mixin called <code>ReactMultiChild</code> which is used by the host internal instance class implementations both in React DOM and React Native.</p></li>
<li><p>The reconciler also implements support for <code>setState()</code> in composite components. Multiple updates inside event handlers get batched into a single update.</p></li>
<li><p>The reconciler also takes care of attaching and detaching refs to composite components and host nodes.</p></li>
<li><p>Lifecycle hooks that are called after the DOM is ready, such as <code>componentDidMount()</code> and <code>componentDidUpdate()</code>, get collected into &quot;callback queues&quot; and are executed in a single batch.</p></li>
<li><p>React puts information about the current update into an internal object called &quot;transaction&quot;. Transactions are useful for keeping track of the queue of pending lifecycle hooks, the current DOM nesting for the warnings, and anything else that is &quot;global&quot; to a specific update. Transactions also ensure React &quot;cleans everything up&quot; after updates. For example, the transaction class provided by React DOM restores the input selection after any update.</p></li>
</ul>
<h3>Jumping into the Code</h3>
<ul>
<li><a href="https://github.com/facebook/react/blob/83381c1673d14cd16cf747e34c945291e5518a86/src/renderers/dom/client/ReactMount.js"><code>ReactMount</code></a> is where the code like <code>mountTree()</code> and <code>unmountTree()</code> from this tutorial lives. It takes care of mounting and unmounting top-level components. <a href="https://github.com/facebook/react/blob/83381c1673d14cd16cf747e34c945291e5518a86/src/renderers/native/ReactNativeMount.js"><code>ReactNativeMount</code></a> is its React Native analog.</li>
<li><a href="https://github.com/facebook/react/blob/83381c1673d14cd16cf747e34c945291e5518a86/src/renderers/dom/shared/ReactDOMComponent.js"><code>ReactDOMComponent</code></a> is the equivalent of <code>DOMComponent</code> in this tutorial. It implements the host component class for React DOM renderer. <a href="https://github.com/facebook/react/blob/83381c1673d14cd16cf747e34c945291e5518a86/src/renderers/native/ReactNativeBaseComponent.js"><code>ReactNativeBaseComponent</code></a> is its React Native analog.</li>
<li><a href="https://github.com/facebook/react/blob/83381c1673d14cd16cf747e34c945291e5518a86/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js"><code>ReactCompositeComponent</code></a> is the equivalent of <code>CompositeComponent</code> in this tutorial. It handles calling user-defined components and maintaining their state.</li>
<li><p><a href="https://github.com/facebook/react/blob/83381c1673d14cd16cf747e34c945291e5518a86/src/renderers/shared/stack/reconciler/instantiateReactComponent.js"><code>instantiateReactComponent</code></a> contains the switch that picks the right internal instance class to construct for an element. It is equivalent to <code>instantiateComponent()</code> in this tutorial.</p></li>
<li><p><a href="https://github.com/facebook/react/blob/83381c1673d14cd16cf747e34c945291e5518a86/src/renderers/shared/stack/reconciler/ReactReconciler.js"><code>ReactReconciler</code></a> is a wrapper with <code>mountComponent()</code>, <code>receiveComponent()</code>, and <code>unmountComponent()</code> methods. It calls the underlying implementations on the internal instances, but also includes some code around them that is shared by all internal instance implementations.</p></li>
<li><p><a href="https://github.com/facebook/react/blob/83381c1673d14cd16cf747e34c945291e5518a86/src/renderers/shared/stack/reconciler/ReactChildReconciler.js"><code>ReactChildReconciler</code></a> implements the logic for mounting, updating, and unmounting children according to the <code>key</code> of their elements.</p></li>
<li><p><a href="https://github.com/facebook/react/blob/83381c1673d14cd16cf747e34c945291e5518a86/src/renderers/shared/stack/reconciler/ReactMultiChild.js"><code>ReactMultiChild</code></a> implements processing the operation queue for child insertions, deletions, and moves independently of the renderer.</p></li>
<li><p><code>mount()</code>, <code>receive()</code>, and <code>unmount()</code> are really called <code>mountComponent()</code>, <code>receiveComponent()</code>, and <code>unmountComponent()</code> in React codebase for legacy reasons, but they receive elements.</p></li>
<li><p>Properties on the internal instances start with an underscore, e.g. <code>_currentElement</code>. They are considered to be read-only public fields throughout the codebase.</p></li>
</ul>
<h3>Future Directions</h3>
<p>Stack reconciler has inherent limitations such as being synchronous and unable to interrupt the work or split it in chunks. There is a work in progress on the <a href="/react/contributing/codebase-overview.html#fiber-reconciler">new Fiber reconciler</a> with a <a href="https://github.com/acdlite/react-fiber-architecture">completely different architecture</a>. In the future, we intend to replace stack reconciler with it, but at the moment it is far from feature parity.</p>
<h3>Next Steps</h3>
<p>Read the <a href="/react/contributing/design-principles.html">next section</a> to learn about the guiding principles we use for React development.</p>
<div class="docs-prevnext">
<a class="docs-prev" href="/react/contributing/codebase-overview.html">&larr; Prev</a>
<a class="docs-next" href="/react/contributing/design-principles.html">Next &rarr;</a>
</div>
</div>
<div class="nav-docs">
<!-- Docs Nav -->
<div class="nav-docs-section">
<h3>Quick Start</h3>
<ul>
<li>
<a href="/react/docs/installation.html">Installation</a>
</li>
<li>
<a href="/react/docs/hello-world.html">Hello World</a>
</li>
<li>
<a href="/react/docs/introducing-jsx.html">Introducing JSX</a>
</li>
<li>
<a href="/react/docs/rendering-elements.html">Rendering Elements</a>
</li>
<li>
<a href="/react/docs/components-and-props.html">Components and Props</a>
</li>
<li>
<a href="/react/docs/state-and-lifecycle.html">State and Lifecycle</a>
</li>
<li>
<a href="/react/docs/handling-events.html">Handling Events</a>
</li>
<li>
<a href="/react/docs/conditional-rendering.html">Conditional Rendering</a>
</li>
<li>
<a href="/react/docs/lists-and-keys.html">Lists and Keys</a>
</li>
<li>
<a href="/react/docs/forms.html">Forms</a>
</li>
<li>
<a href="/react/docs/lifting-state-up.html">Lifting State Up</a>
</li>
<li>
<a href="/react/docs/composition-vs-inheritance.html">Composition vs Inheritance</a>
</li>
<li>
<a href="/react/docs/thinking-in-react.html">Thinking In React</a>
</li>
</ul>
</div>
<div class="nav-docs-section">
<h3>Advanced Guides</h3>
<ul>
<li>
<a href="/react/docs/jsx-in-depth.html">JSX In Depth</a>
</li>
<li>
<a href="/react/docs/typechecking-with-proptypes.html">Typechecking With PropTypes</a>
</li>
<li>
<a href="/react/docs/refs-and-the-dom.html">Refs and the DOM</a>
</li>
<li>
<a href="/react/docs/uncontrolled-components.html">Uncontrolled Components</a>
</li>
<li>
<a href="/react/docs/optimizing-performance.html">Optimizing Performance</a>
</li>
<li>
<a href="/react/docs/react-without-es6.html">React Without ES6</a>
</li>
<li>
<a href="/react/docs/react-without-jsx.html">React Without JSX</a>
</li>
<li>
<a href="/react/docs/reconciliation.html">Reconciliation</a>
</li>
<li>
<a href="/react/docs/context.html">Context</a>
</li>
<li>
<a href="/react/docs/portals.html">Portals</a>
</li>
<li>
<a href="/react/docs/web-components.html">Web Components</a>
</li>
<li>
<a href="/react/docs/higher-order-components.html">Higher-Order Components</a>
</li>
<li>
<a href="/react/docs/integrating-with-other-libraries.html">Integrating with Other Libraries</a>
</li>
<li>
<a href="/react/docs/accessibility.html">Accessibility</a>
</li>
</ul>
</div>
<div class="nav-docs-section">
<h3>Reference</h3>
<ul>
<li>
<a href="/react/docs/react-api.html">React</a>
<ul>
<li>
<a href="/react/docs/react-component.html">React.Component</a>
</li>
</ul>
</li>
<li>
<a href="/react/docs/react-dom.html">ReactDOM</a>
</li>
<li>
<a href="/react/docs/react-dom-server.html">ReactDOMServer</a>
</li>
<li>
<a href="/react/docs/dom-elements.html">DOM Elements</a>
</li>
<li>
<a href="/react/docs/events.html">SyntheticEvent</a>
</li>
<li>
<a href="/react/docs/test-utils.html">Test Utilities</a>
</li>
<li>
<a href="/react/docs/shallow-renderer.html">Shallow Renderer</a>
</li>
<li>
<a href="/react/docs/test-renderer.html">Test Renderer</a>
</li>
<li>
<a href="/react/docs/javascript-environment-requirements.html">JS Environment Requirements</a>
</li>
</ul>
</div>
<!-- Contributing Nav -->
<div class="nav-docs-section">
<h3>Contributing</h3>
<ul>
<li>
<a href="/react/contributing/how-to-contribute.html">How to Contribute</a>
</li>
<li>
<a href="/react/contributing/codebase-overview.html">Codebase Overview</a>
</li>
<li>
<a href="/react/contributing/implementation-notes.html" class="active">Implementation Notes</a>
</li>
<li>
<a href="/react/contributing/design-principles.html">Design Principles</a>
</li>
</ul>
</div>
</div>
</section>
<footer class="nav-footer">
<section class="sitemap">
<a href="/react/" class="nav-home">
</a>
<div>
<h5><a href="/react/docs/">Docs</a></h5>
<a href="/react/docs/hello-world.html">Quick Start</a>
<a href="/react/docs/thinking-in-react.html">Thinking in React</a>
<a href="/react/tutorial/tutorial.html">Tutorial</a>
<a href="/react/docs/jsx-in-depth.html">Advanced Guides</a>
</div>
<div>
<h5><a href="/react/community/support.html">Community</a></h5>
<a href="http://stackoverflow.com/questions/tagged/reactjs" target="_blank">Stack Overflow</a>
<a href="https://discuss.reactjs.org/" target="_blank">Discussion Forum</a>
<a href="https://discord.gg/0ZcbPKXt5bZjGY5n" target="_blank">Reactiflux Chat</a>
<a href="https://www.facebook.com/react" target="_blank">Facebook</a>
<a href="https://twitter.com/reactjs" target="_blank">Twitter</a>
</div>
<div>
<h5><a href="/react/community/support.html">Resources</a></h5>
<a href="/react/community/conferences.html">Conferences</a>
<a href="/react/community/videos.html">Videos</a>
<a href="https://github.com/facebook/react/wiki/Examples" target="_blank">Examples</a>
<a href="https://github.com/facebook/react/wiki/Complementary-Tools" target="_blank">Complementary Tools</a>
</div>
<div>
<h5>More</h5>
<a href="/react/blog/">Blog</a>
<a href="https://github.com/facebook/react" target="_blank">GitHub</a>
<a href="http://facebook.github.io/react-native/" target="_blank">React Native</a>
<a href="/react/acknowledgements.html">Acknowledgements</a>
</div>
</section>
<a href="https://code.facebook.com/projects/" target="_blank" class="fbOpenSource">
<img src="/react/img/oss_logo.png" alt="Facebook Open Source" width="170" height="45"/>
</a>
<section class="copyright">
Copyright © 2017 Facebook Inc.
</section>
</footer>
</div>
<div id="fb-root"></div>
<script src="/react/js/anchor-links.js"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-41298772-1', 'facebook.github.io');
ga('send', 'pageview');
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.6&appId=623268441017527";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
docsearch({
apiKey: '36221914cce388c46d0420343e0bb32e',
indexName: 'react',
inputSelector: '#algolia-doc-search'
});
</script>
</body>
</html>