From 5e1b58833b11219b86a72914e1cb539e769eabd1 Mon Sep 17 00:00:00 2001 From: Thomas Hintz Date: Thu, 8 Oct 2020 09:31:35 -0700 Subject: [PATCH] Incorporating Tim's feedback. --- foundations-high-performance-react.org | 48 +++++++++++-------- manuscript/components-of-react.markua | 4 +- manuscript/fibers.markua | 2 +- ...eady-to-render-with--createelement-.markua | 4 +- manuscript/preface.markua | 4 +- manuscript/reconciliation.markua | 4 +- manuscript/render.markua | 2 +- 7 files changed, 39 insertions(+), 29 deletions(-) diff --git a/foundations-high-performance-react.org b/foundations-high-performance-react.org index 9d9259c..bdbff59 100644 --- a/foundations-high-performance-react.org +++ b/foundations-high-performance-react.org @@ -30,16 +30,14 @@ gain an understanding of the real React and how to build high-performance applications with it. This book is based on the first chapter of the book /High-Performance -React/. If you enjoy /Foundations of High-Performance React -Applications/ and you want to learn more practical ways to utilize the -foundations we will learn here and get a more detailed blue-print for -creating high performance React applications, then be sure to check -out /High-Performance React/. +React/. If you enjoy this book and you want to learn more practical +ways to utilize the foundations we will learn here and get a more +detailed blue-print for creating high performance React applications, +then be sure to check out /High-Performance React/. -/Foundations of High-Performance React Applications/ is not intended -to be an introduction to React or JavaScript. While it might be useful -to beginners, this book assumes familiarity with both JavaScript and -React. +This book is not intended to be an introduction to React or +JavaScript. While it might be useful to beginners, this book assumes +familiarity with both JavaScript and React. And while this book only specifically addresses React-DOM the foundations apply equally to React-Native and other React @@ -109,11 +107,17 @@ does. :EXPORT_FILE_NAME: manuscript/components-of-react.markua :END: -Conceptually React is very simple. It starts by walking a tree of -components and building up a tree of their output. Then it compares -that tree to the tree currently in the browser's DOM to find any -differences between them. When it finds differences it updates the -browser's DOM to match its internal tree. +The primary elements that make up any React program are its +components. A ~component~ in React maintains local state and "renders" +output to eventually be included in the browser's DOM. A tree of +components is then created whenever a component outputs other +components. + +So, conceptually, React's core algorithm is very simple: it starts by +walking a tree of components and building up a tree of their +output. Then it compares that tree to the tree currently in the +browser's DOM to find any differences between them. When it finds +differences it updates the browser's DOM to match its internal tree. But what does that actually look like? If your app is janky does that explanation point you towards what is wrong? No. It might make you @@ -291,8 +295,8 @@ are not relevant to our study here. #+BEGIN_SRC javascript function createElement(node) { - // if array (not text, number, or other primitive) - if (typeof node === 'object') { + // if array (our representation of an element) + if (Array.isArray(node)) { const [ tag, props, children ] = node; return { type: tag, @@ -326,7 +330,7 @@ of ~elements~ (surprise). That's it. Now we have everything we need to actually begin the process of rendering our tree to the DOM! -* Render +* Render: Putting Elements on the Screen :PROPERTIES: :EXPORT_FILE_NAME: manuscript/render.markua :END: @@ -403,7 +407,7 @@ tree in the browser's DOM! But so far we can only add things to our tree. To be able to remove and modify the tree we need one more part: reconciliation. -* Reconciliation +* Reconciliation, or How React Diffs :PROPERTIES: :EXPORT_FILE_NAME: manuscript/reconciliation.markua :END: @@ -495,7 +499,11 @@ is the DOM element associated with our synthetic element and ~parent~ is a reference to the parent DOM element. Here we begin by adding a global object that will store our last render -tree, keyed by the ~container~. +tree, keyed by the ~container~. ~container~ refers to the browser's +DOM element that will be the parent for all of the React derived DOM +elements. This parent DOM element can only be used to render one tree +of elements at a time so it works well to use as a key for +~renderTrees~. #+BEGIN_SRC javascript const renderTrees = {}; @@ -692,7 +700,7 @@ simulates, you'll be hitting a major performance bottleneck since React will not only be replacing DOM elements in the browser but also tearing down and rebuilding the trees of child components. -* Fibers +* Fibers: Splitting up Render :PROPERTIES: :EXPORT_FILE_NAME: manuscript/fibers.markua :END: diff --git a/manuscript/components-of-react.markua b/manuscript/components-of-react.markua index 130ad40..6ae132c 100644 --- a/manuscript/components-of-react.markua +++ b/manuscript/components-of-react.markua @@ -1,6 +1,8 @@ # Components of React -Conceptually React is very simple. It starts by walking a tree of components and building up a tree of their output. Then it compares that tree to the tree currently in the browser's DOM to find any differences between them. When it finds differences it updates the browser's DOM to match its internal tree. +The primary elements that make up any React program are its components. A `component` in React maintains local state and "renders" output to eventually be included in the browser's DOM. A tree of components is then created whenever a component outputs other components. + +So, conceptually, React's core algorithm is very simple: it starts by walking a tree of components and building up a tree of their output. Then it compares that tree to the tree currently in the browser's DOM to find any differences between them. When it finds differences it updates the browser's DOM to match its internal tree. But what does that actually look like? If your app is janky does that explanation point you towards what is wrong? No. It might make you wonder if maybe it is too expensive to re-render the tree or if maybe the diffing React does is slow but you won't really know. When I was initially testing out different bread recipes I had guesses at why it wasn't working but I didn't really figure it out until I had a deeper understanding of how making bread worked. It's time we build up our understanding of how React works so that we can start to answer our questions with solid answers. diff --git a/manuscript/fibers.markua b/manuscript/fibers.markua index 57f25e2..2100a6f 100644 --- a/manuscript/fibers.markua +++ b/manuscript/fibers.markua @@ -1,4 +1,4 @@ -# Fibers +# Fibers: Splitting up Render The actual React implementation used to look very similar to what we've built so far, but with React 16 this has changed dramatically with the introduction of Fibers. Fibers are a name that React gives to discrete units of work during the render process. And the React reconciliation algorithm was changed to be based on small units of work instead of one large, potentially long-running call to `render`. This means that React is now able to process just part of the render phase, pause to let the browser take care of other things, and resume again. This is the underlying change the enables the experimental Concurrent Mode as well as running most hooks without blocking the render. diff --git a/manuscript/getting-ready-to-render-with--createelement-.markua b/manuscript/getting-ready-to-render-with--createelement-.markua index 3228e1b..e80d3d5 100644 --- a/manuscript/getting-ready-to-render-with--createelement-.markua +++ b/manuscript/getting-ready-to-render-with--createelement-.markua @@ -22,8 +22,8 @@ That is: an object with two properties: `type` and `props`. The `props` property {format: "javascript"} ``` function createElement(node) { - // if array (not text, number, or other primitive) - if (typeof node === 'object') { + // if array (our representation of an element) + if (Array.isArray(node)) { const [ tag, props, children ] = node; return { type: tag, diff --git a/manuscript/preface.markua b/manuscript/preface.markua index 2805fc4..47027e0 100644 --- a/manuscript/preface.markua +++ b/manuscript/preface.markua @@ -2,9 +2,9 @@ Welcome to *Foundations of High-Performance React Applications* where we build our own simplified version of React. We will use our React to gain an understanding of the real React and how to build high-performance applications with it. -This book is based on the first chapter of the book *High-Performance React*. If you enjoy *Foundations of High-Performance React Applications* and you want to learn more practical ways to utilize the foundations we will learn here and get a more detailed blue-print for creating high performance React applications, then be sure to check out *High-Performance React*. +This book is based on the first chapter of the book *High-Performance React*. If you enjoy this book and you want to learn more practical ways to utilize the foundations we will learn here and get a more detailed blue-print for creating high performance React applications, then be sure to check out *High-Performance React*. -*Foundations of High-Performance React Applications* is not intended to be an introduction to React or JavaScript. While it might be useful to beginners, this book assumes familiarity with both JavaScript and React. +This book is not intended to be an introduction to React or JavaScript. While it might be useful to beginners, this book assumes familiarity with both JavaScript and React. And while this book only specifically addresses React-DOM the foundations apply equally to React-Native and other React implementations because they are all based on the same core React library and algorithms. diff --git a/manuscript/reconciliation.markua b/manuscript/reconciliation.markua index c2eea6b..f0b523d 100644 --- a/manuscript/reconciliation.markua +++ b/manuscript/reconciliation.markua @@ -1,4 +1,4 @@ -# Reconciliation +# Reconciliation, or How React Diffs A tale of two trees. These are the two trees that people most often talk about when talking about React's "secret sauce": the virtual DOM and the browser's DOM tree. This idea is what originally set React apart. React's reconciliation is what allows you to program declaratively. Reconciliation is what makes it so we no longer have to manually update and modify the DOM whenever our own internal state changes. In a lot of ways, it is what makes React, React. @@ -39,7 +39,7 @@ Notice that in every case, except deletion, we still call `render` on the elemen Now, to get started with our render method we must make some modifications to our previous render method. First, we need to be able to store and retrieve the previous render tree. Then we need to add code to compare parts of the tree to decide if we can re-use DOM elements from the previous render tree. And last, we need to return a tree of elements that can be used in the next render as a comparison and to reference the DOM elements that we create. These new element objects will have the same structure as our current elements but we will add two new properties: `domElement` and `parent`. `domElement` is the DOM element associated with our synthetic element and `parent` is a reference to the parent DOM element. -Here we begin by adding a global object that will store our last render tree, keyed by the `container`. +Here we begin by adding a global object that will store our last render tree, keyed by the `container`. `container` refers to the browser's DOM element that will be the parent for all of the React derived DOM elements. This parent DOM element can only be used to render one tree of elements at a time so it works well to use as a key for `renderTrees`. {format: "javascript"} ``` diff --git a/manuscript/render.markua b/manuscript/render.markua index b01e83f..eea7c84 100644 --- a/manuscript/render.markua +++ b/manuscript/render.markua @@ -1,4 +1,4 @@ -# Render +# Render: Putting Elements on the Screen There are now only two major puzzles remaining in our quest for our own React. The next piece is: `render`. How do we go from our JSM tree of nodes, to actually displaying something on screen? To do that we will explore the `render` method.