@ -12,6 +12,7 @@
 
		
	
		
			
				
 
		
	
		
			
				#+TITLE : Foundations of High-Performance React Applications  
		
	
		
			
				#+AUTHOR : Thomas Hintz  
		
	
		
			
				#+EXCLUDE_TAGS : noexport  
		
	
		
			
				
 
		
	
		
			
				#+startup : indent  
		
	
		
			
				#+tags : noexport sample frontmatter mainmatter backmatter  
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -196,7 +197,8 @@ This is what I'm thinking:
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				['div', { 'className': 'header' },
 
		
	
		
			
				   [['h1', {}, ['Hello']],
 
		
	
		
			
				    ['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
 
		
	
		
			
				    ['input', { 'type': 'submit', 'disabled': 'disabled' },
 
		
	
		
			
				     []]
 
		
	
		
			
				   ]
 
		
	
		
			
				]
 
		
	
		
			
				#+END_SRC
 
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -345,10 +347,10 @@ focusing on the initial render. In pseudocode it looks like this:
 
		
	
		
			
				
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				function render(element, container) {
 
		
	
		
			
				     const domElement = createDOMElement(element);
 
		
	
		
			
				     setProps(element, domElement);
 
		
	
		
			
				     renderChildren(element, domElement);
 
		
	
		
			
				     container.appendChild(domElement);
 
		
	
		
			
				   const domElement = createDOMElement(element);
 
		
	
		
			
				   setProps(element, domElement);
 
		
	
		
			
				   renderChildren(element, domElement);
 
		
	
		
			
				   container.appendChild(domElement);
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
		
			
				Our DOM element is created first. Then we set the properties, render
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -367,23 +369,24 @@ exploring it after we work out the initial render.
 
		
	
		
			
				
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				function render(element, container) {
 
		
	
		
			
				     const { type, props } = element;
 
		
	
		
			
				   const { type, props } = element;
 
		
	
		
			
				
 
		
	
		
			
				     // create the DOM element
 
		
	
		
			
				     const domElement = type = == 'TEXT' ?
 
		
	
		
			
				       document.createTextNode(props.nodeValue) :
 
		
	
		
			
				       document.createElement(type);
 
		
	
		
			
				   // create the DOM element
 
		
	
		
			
				   const domElement = type = == 'TEXT' ?
 
		
	
		
			
				     document.createTextNode(props.nodeValue) :
 
		
	
		
			
				     document.createElement(type);
 
		
	
		
			
				
 
		
	
		
			
				     // set its properties
 
		
	
		
			
				     Object.keys(props)
 
		
	
		
			
				       .filter((key) => key != = 'children')
 
		
	
		
			
				       .forEach((key) => domElement[key] =  props[key]);
 
		
	
		
			
				   // set its properties
 
		
	
		
			
				   Object.keys(props)
 
		
	
		
			
				     .filter((key) => key != = 'children')
 
		
	
		
			
				     .forEach((key) => domElement[key] =  props[key]);
 
		
	
		
			
				
 
		
	
		
			
				    // render its children
 
		
	
		
			
				    props.children.forEach((child) => render(child, domElement));
 
		
	
		
			
				   // render its children
 
		
	
		
			
				   props.children.forEach((child) =>
 
		
	
		
			
				      render(child, domElement));
 
		
	
		
			
				
 
		
	
		
			
				     // add our tree to the DOM!
 
		
	
		
			
				     container.appendChild(domElement);
 
		
	
		
			
				   // add our tree to the DOM!
 
		
	
		
			
				   container.appendChild(domElement);
 
		
	
		
			
				}
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -464,14 +467,14 @@ from the current tree and ~prevElement~ is the corresponding element
 
		
	
		
			
				in the tree from the previous render.
 
		
	
		
			
				
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				     if (!element && prevElement) 
		
	
		
			
				       // delete dom element 
		
	
		
			
				     else if (element && !prevElement) 
		
	
		
			
				       // add new dom element, render children 
		
	
		
			
				     else if (element.type === prevElement.type) 
		
	
		
			
				       // update dom element, render children 
		
	
		
			
				     else if (element.type !== prevElement.type) 
		
	
		
			
				       // replace dom element, render children 
		
	
		
			
				if (!element && prevElement)
 
		
	
		
			
				   // delete dom element
 
		
	
		
			
				else if (element && !prevElement)
 
		
	
		
			
				   // add new dom element, render children
 
		
	
		
			
				else if (element.type === prevElement.type)
 
		
	
		
			
				   // update dom element, render children
 
		
	
		
			
				else if (element.type !== prevElement.type)
 
		
	
		
			
				   // replace dom element, render children
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
		
			
				Notice that in every case, except deletion, we still call ~render~  on
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -497,10 +500,10 @@ tree, keyed by the ~container~.
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				const renderTrees = {};
 
		
	
		
			
				function render(element, container) {
 
		
	
		
			
				     const tree =
 
		
	
		
			
				       render_internal(element, container, renderTrees[container]);
 
		
	
		
			
				     // render complete, store the updated tree
 
		
	
		
			
				     renderTrees[container] = tree;
 
		
	
		
			
				   const tree =
 
		
	
		
			
				     render_internal(element, container, renderTrees[container]);
 
		
	
		
			
				   // render complete, store the updated tree
 
		
	
		
			
				   renderTrees[container] = tree;
 
		
	
		
			
				}
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -522,34 +525,34 @@ delete a node that previously existed.
 
		
	
		
			
				
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				function render_internal(element, container, prevElement) {
 
		
	
		
			
				     let domElement, children;
 
		
	
		
			
				     if (!element && prevElement) {
 
		
	
		
			
				         removeDOMElement(prevElement);
 
		
	
		
			
				         return;
 
		
	
		
			
				     } else if (element && !prevElement) {
 
		
	
		
			
				         domElement = createDOMElement(element);
 
		
	
		
			
				     } else if (element.type === prevElement.type) {
 
		
	
		
			
				         domElement = prevElement.domElement;
 
		
	
		
			
				     } else { // types don't match
 
		
	
		
			
				         removeDOMElement(prevElement);
 
		
	
		
			
				         domElement = createDOMElement(element);
 
		
	
		
			
				     }
 
		
	
		
			
				     setDOMProps(element, domElement, prevElement);
 
		
	
		
			
				     children = renderChildren(element, domElement, prevElement);
 
		
	
		
			
				
 
		
	
		
			
				     if (!prevElement || domElement !== prevElement.domElement) {
 
		
	
		
			
				         container.appendChild(domElement);
 
		
	
		
			
				     }
 
		
	
		
			
				
 
		
	
		
			
				     return {
 
		
	
		
			
				         domElement: domElement,
 
		
	
		
			
				         parent: container,
 
		
	
		
			
				         type: element.type,
 
		
	
		
			
				         props: {
 
		
	
		
			
				             ...element.props,
 
		
	
		
			
				             children: children
 
		
	
		
			
				         }
 
		
	
		
			
				     };
 
		
	
		
			
				   let domElement, children;
 
		
	
		
			
				   if (!element && prevElement) {
 
		
	
		
			
				      removeDOMElement(prevElement);
 
		
	
		
			
				      return;
 
		
	
		
			
				   } else if (element && !prevElement) {
 
		
	
		
			
				      domElement = createDOMElement(element);
 
		
	
		
			
				   } else if (element.type === prevElement.type) {
 
		
	
		
			
				      domElement = prevElement.domElement;
 
		
	
		
			
				   } else { // types don't match
 
		
	
		
			
				      removeDOMElement(prevElement);
 
		
	
		
			
				      domElement = createDOMElement(element);
 
		
	
		
			
				   }
 
		
	
		
			
				   setDOMProps(element, domElement, prevElement);
 
		
	
		
			
				   children = renderChildren(element, domElement, prevElement);
 
		
	
		
			
				
 
		
	
		
			
				   if (!prevElement || domElement !== prevElement.domElement) {
 
		
	
		
			
				      container.appendChild(domElement);
 
		
	
		
			
				   }
 
		
	
		
			
				
 
		
	
		
			
				   return {
 
		
	
		
			
				      domElement: domElement,
 
		
	
		
			
				      parent: container,
 
		
	
		
			
				      type: element.type,
 
		
	
		
			
				      props: {
 
		
	
		
			
				         ...element.props,
 
		
	
		
			
				         children: children
 
		
	
		
			
				      }
 
		
	
		
			
				   };
 
		
	
		
			
				}
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -577,7 +580,7 @@ the parent element. Before removing the element, React would invoke
 
		
	
		
			
				
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				function removeDOMElement(prevElement) {
 
		
	
		
			
				     prevElement.parent.removeChild(prevElement.domElement);
 
		
	
		
			
				   prevElement.parent.removeChild(prevElement.domElement);
 
		
	
		
			
				}
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -590,9 +593,9 @@ will set it again. This is where React would invoke
 
		
	
		
			
				
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				function createDOMElement(element) {
 
		
	
		
			
				     return element.type === 'TEXT' ?
 
		
	
		
			
				       document.createTextNode(element.props.nodeValue) :
 
		
	
		
			
				       document.createElement(element.type);
 
		
	
		
			
				   return element.type === 'TEXT' ?
 
		
	
		
			
				     document.createTextNode(element.props.nodeValue) :
 
		
	
		
			
				     document.createElement(element.type);
 
		
	
		
			
				}
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -603,18 +606,18 @@ and it isn't intended to be set directly.
 
		
	
		
			
				
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				function setDOMProps(element, domElement, prevElement) {
 
		
	
		
			
				     if (prevElement) {
 
		
	
		
			
				         Object.keys(prevElement.props)
 
		
	
		
			
				             .filter((key) => key != = 'children')
 
		
	
		
			
				             .forEach((key) => {
 
		
	
		
			
				                 domElement[key] = ''; // clear prop
 
		
	
		
			
				             });
 
		
	
		
			
				     }
 
		
	
		
			
				     Object.keys(element.props)
 
		
	
		
			
				       .filter((key) => key != = 'children')
 
		
	
		
			
				       .forEach((key) => {
 
		
	
		
			
				           domElement[key] = element.props[key];
 
		
	
		
			
				     });
 
		
	
		
			
				   if (prevElement) {
 
		
	
		
			
				      Object.keys(prevElement.props)
 
		
	
		
			
				          .filter((key) => key != = 'children')
 
		
	
		
			
				          .forEach((key) => {
 
		
	
		
			
				              domElement[key] = ''; // clear prop
 
		
	
		
			
				          });
 
		
	
		
			
				   }
 
		
	
		
			
				   Object.keys(element.props)
 
		
	
		
			
				     .filter((key) => key != = 'children')
 
		
	
		
			
				     .forEach((key) => {
 
		
	
		
			
				        domElement[key] = element.props[key];
 
		
	
		
			
				   });
 
		
	
		
			
				}
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -640,17 +643,17 @@ corresponding element, like when the list of children has grown.
 
		
	
		
			
				
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				function renderChildren(element, domElement, prevElement = { props: { children: [] }}) {
 
		
	
		
			
				     const elementLen = element.props.children.length;
 
		
	
		
			
				     const prevElementLen = prevElement.props.children.length;
 
		
	
		
			
				     // remove now unused elements
 
		
	
		
			
				     for (let i = elementLen; i < prevElementLen - elementLen; i++) {
 
		
	
		
			
				         removeDOMElement(element.props.children[i]);
 
		
	
		
			
				     }
 
		
	
		
			
				     // render existing and new elements
 
		
	
		
			
				     return element.props.children.map((child, i) => {
 
		
	
		
			
				         const prevChild = i < prevElementLen ? prevElement.props.children[i] : undefined;
 
		
	
		
			
				         return render_internal(child, domElement, prevChild);
 
		
	
		
			
				     });
 
		
	
		
			
				   const elementLen = element.props.children.length;
 
		
	
		
			
				   const prevElementLen = prevElement.props.children.length;
 
		
	
		
			
				   // remove now unused elements
 
		
	
		
			
				   for (let i = elementLen; i < prevElementLen - elementLen; i++) {
 
		
	
		
			
				      removeDOMElement(element.props.children[i]);
 
		
	
		
			
				   }
 
		
	
		
			
				   // render existing and new elements
 
		
	
		
			
				   return element.props.children.map((child, i) => {
 
		
	
		
			
				      const prevChild = i < prevElementLen ? prevElement.props.children[i] : undefined;
 
		
	
		
			
				      return render_internal(child, domElement, prevChild);
 
		
	
		
			
				   });
 
		
	
		
			
				}
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -719,19 +722,19 @@ At this point the only thing left to do is to create some components
 
		
	
		
			
				and use them!
 
		
	
		
			
				
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				   const SayNow = ({ dateTime }) = > { 
		
	
		
			
				       return ['h1', {}, [`It is: ${dateTime}`]]; 
		
	
		
			
				   }; 
		
	
		
			
				
 
		
	
		
			
				   const App = () = > { 
		
	
		
			
				       return ['div', { 'className': 'header' }, 
		
	
		
			
				               [SayNow({ dateTime: new Date() }),
 
		
	
		
			
				                ['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
 
		
	
		
			
				               ]
 
		
	
		
			
				              ];
 
		
	
		
			
				   } 
		
	
		
			
				const SayNow = ({ dateTime }) = > {
 
		
	
		
			
				   return ['h1', {}, [`It is: ${dateTime}`]];
 
		
	
		
			
				};
 
		
	
		
			
				
 
		
	
		
			
				const App = () = > {
 
		
	
		
			
				   return ['div', { 'className': 'header' },
 
		
	
		
			
				           [SayNow({ dateTime: new Date() }),
 
		
	
		
			
				            ['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
 
		
	
		
			
				           ]
 
		
	
		
			
				          ];
 
		
	
		
			
				}
 
		
	
		
			
				
 
		
	
		
			
				   render(createElement(App()), document.getElementById('root')); 
		
	
		
			
				render(createElement(App()), document.getElementById('root'));
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
		
			
				We are creating two components, that output JSM, as we defined it
 
		
	
	
		
			
				
					
						
						
						
							
								 
						
					 
				
			
			@ -745,8 +748,8 @@ The next step is to call render multiple times.
 
		
	
		
			
				
 
		
	
		
			
				#+BEGIN_SRC javascript
 
		
	
		
			
				setInterval(() =>
 
		
	
		
			
				  render(createElement(App()), document.getElementById('root')),
 
		
	
		
			
				  1000);
 
		
	
		
			
				    render(createElement(App()), document.getElementById('root')),
 
		
	
		
			
				    1000);
 
		
	
		
			
				#+END_SRC
 
		
	
		
			
				
 
		
	
		
			
				If you run the code above you will see the DateTime display being
 
		
	
	
		
			
				
					
						
							
								 
						
						
							
								 
						
						
					 
				
			
			@ -787,7 +790,7 @@ cause of a performance bottleneck? Or how do you use the React APIs in
 
		
	
		
			
				a performant way? These types of questions should be easier to track
 
		
	
		
			
				down and understand with the foundations covered and I hope this is
 
		
	
		
			
				only the start of your High-Performance React journey.
 
		
	
		
			
				*  Image Test  
		
	
		
			
				*  Image Test                                                        :noexport:   
		
	
		
			
				:PROPERTIES:
 
		
	
		
			
				:EXPORT_FILE_NAME: manuscript/image-test.markua
 
		
	
		
			
				:END: