From 7c496c43407a2b9f333cbb71e6e3d00c5494c866 Mon Sep 17 00:00:00 2001 From: Thomas Hintz Date: Sun, 2 May 2021 07:25:55 -0700 Subject: [PATCH] Incorporating Brian's proofing edits. --- ...h-performance-react - Brian C proofed.docx | Bin 0 -> 48291 bytes foundations-high-performance-react.org | 276 +++++++++--------- 2 files changed, 137 insertions(+), 139 deletions(-) create mode 100644 foundations-high-performance-react - Brian C proofed.docx diff --git a/foundations-high-performance-react - Brian C proofed.docx b/foundations-high-performance-react - Brian C proofed.docx new file mode 100644 index 0000000000000000000000000000000000000000..2f5161cc10a1adc0b702c28e5d100c0803ec3e32 GIT binary patch literal 48291 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 literal 0 HcmV?d00001 diff --git a/foundations-high-performance-react.org b/foundations-high-performance-react.org index 7087c36..34c760f 100644 --- a/foundations-high-performance-react.org +++ b/foundations-high-performance-react.org @@ -25,56 +25,56 @@ :END: Welcome to /Foundations of High-Performance React Applications/ where -we build our own simplified version of React. We will use our React to +we build our own simplified version of React. We’ll 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 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, +ways to utilize the foundations we’ll learn here and get a more +detailed blueprint for creating high performance React applications, then be sure to check out /High-Performance 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 +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. -For the code in this book, the goal is to be clear and simple; to best -communicate the algorithms we will be exploring. It is not intended to -be used in production but it is functional and you will likely find it -useful to follow along by also writing the code yourself. It will help +The code in this book is clear and simple so as to best +communicate the algorithms we’ll be exploring. It is not intended to +be used in production, but it is functional. I think you’ll likely find it +useful to follow along by writing the code yourself. It will help you better understand how it works, and even more critically, it will allow you to play with it and test how the algorithms work with your own examples. -Even if you don't write out the code yourself and instead treat this -book more as a novel you read through and then move on, I believe the -fundamentals should still stick with you and provide value in your +Even if you don't write out the code yourself and,instead, read through this +book more like a novel, I believe the +fundamentals will still stick with you and provide value in your React programs-to-come. -I'm very excited to take you on this journey with me and now it's time -to learn what lays at the very foundation of React. +I'm very excited to take you on this journey with me and, so, now it's time +to learn what lies at the very foundation of React. * Acknowledgments :PROPERTIES: :EXPORT_FILE_NAME: manuscript/acknowledgments.markua :END: -First, I'd like to thank my partner Laura, for always supporting me in -whatever endeavors I embark upon even if they're new, challenging, or -scary. This book, nor my work with React, would have taken place if it +First, I'd like to thank my partner Laura for always supporting me in +whatever endeavors I embark upon, whether they're new, challenging, or +scary. This book and my work with React wouldn’t have taken place if it weren't for her support and strength. -Second, I would like to thank my friend, Timothy Licata, for providing +Second, I would like to thank my friend Timothy Licata for providing invaluable feedback on earlier versions and always pushing me to explore new ways of using Emacs, such as writing this book. And last, I would like to thank the wider JavaScript and React -community for providing many years of work to build upon and +community for providing many years of work to build upon, specifically Rodrigo Pombo's "Build Your Own React" for being the inspiration for a lot of this book's content. * Introduction :mainmatter: @@ -82,40 +82,39 @@ inspiration for a lot of this book's content. :EXPORT_FILE_NAME: manuscript/introduction.markua :END: -Baking bread. When I first began to learn how to bake bread the recipe -told me what to do. It listed some ingredients and told me how to -combine them and prescribed times of rest. It gave me an oven +When I first began to learn how to bake bread, a recipe +told me what to do. It listed some ingredients, told me how to +combine them, and prescribed times of rest. It gave me an oven temperature and a period of wait. It gave me mediocre bread of wildly -varying quality. I tried different recipes but the result was always +varying quality. I tried different recipes, but the result was always the same. Understanding: that's what I was missing. The bread I make is now consistently good. The recipes I use are simpler and only give ratios -and general recommendations for rests and waits. So why does the bread -turn out better? - -Before baking is finished bread is a living organism. The way it grows -and develops and flavors depend on what you feed it and how you feed -it and massage it, care for it. If you have it grow and ferment at a -higher temperature and more yeast it overdevelops producing too much -alcohol. If you give it too much time, acidity will take over the -flavor. The recipes I used initially were missing a critical -ingredient: the rising temperature. - -But unlike a lot of ingredients: temperature is hard to control for -the home cook. So the recipe can't just tell you exactly what -temperature to grow the bread at. My initial recipes just silently -made assumptions for the temperature, which rarely turn out to be -true. This means that the only way to consistently make good bread is -to have an understanding of how bread develops so that you can adjust -the other ingredients to complement the temperature. Now the bread can -tell me what to do. +and general recommendations for rests and waits. So, why does the bread +now turn out better? + +Before it is baked, bread is a living organism. So, the way it grows, +develops, and flavors depends on what you feed it, how you feed it, +how you massage it, and how you care for it. If you have it grow and +ferment with more yeast at a higher temperature, it +overdevelops, producing too much alcohol. If you give it too much time, +acidity will take over the flavor. The recipes I used initially were +missing a critical ingredient: the rising temperature. + +But unlike other ingredients, temperature is hard for the home cook to +control. And recipes don’t say exactly at which temperature to grow +the bread. My initial recipes just silently made assumptions about the +temperature, which rarely worked. This means the only way to +consistently make good bread is to have an understanding of how bread +develops so that you can adjust the other ingredients to complement +the temperature. Now the bread can tell me what to do. While React isn't technically a living organism that can tell us what to do, it is, in its whole, a complex, abstract entity. We could learn -basic recipes for how to write high-performance React code but they -wouldn't apply in all cases, and as React and things under it change -our recipes would fall out-of-date. So like the bread, to produce +basic recipes for how to write high-performance React code, but they +wouldn't apply in all cases. And as React and things under it change, +our recipes would fall out-of-date. So, like bread, to produce consistently good results we need to understand how React does what it does. @@ -139,19 +138,19 @@ 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 +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 +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. -React is centered around the ~render~ method. The ~render~ method is +React is centered on the ~render~ method. The ~render~ method is what walks our trees, diffs them with the browser's DOM tree, and updates the DOM as needed. But before we can look at the ~render~ method we have to understand its input. The input comes from ~createElement~. While ~createElement~ itself is unlikely to be a -bottleneck it's good to understand how it works so that we can have a +bottleneck, it's good to understand how it works so that we can have a complete picture of the entire process. The more black-boxes we have in our mental model the harder it will be for us to diagnose performance problems. @@ -165,14 +164,14 @@ performance problems. not familiar to us since we usually work in JSX, which is the last element of the chain in this puzzle and the first step in solving it. While not strictly a part of React, it is almost universally used -with it. And if we understand it then ~createElement~ will be less of -a mystery since we will be able to connect all the dots. +with it. And if we understand it, ~createElement~ will then be less of +a mystery since we’ll be able to connect all the dots. JSX is not valid HTML or JavaScript but its own language compiled by a compiler, like Babel. The output of that compilation is valid JavaScript that represents the original markup. -Before JSX or similar compilers, the normal way of injecting HTML into +Before JSX or similar compilers, the typical way of injecting HTML into the DOM was via directly utilizing the browser's DOM APIs or by setting ~innerHTML~. This was very cumbersome. The code's structure did not match the structure of the HTML that it output which made it @@ -180,12 +179,12 @@ hard to quickly understand what the output of a piece of code would be. So naturally programmers have been endlessly searching for better ways to mix HTML with JavaScript. -And this brings us to JSX. It is nothing new; nothing +And this brings us to JSX. It is nothing new, nothing complicated. Forms of it have been made and used long before React adopted it. Now let's see if we can discover JSX for ourselves. -To start with, we need to create a data-structure -- let's call it -JavaScript Markup (JSM) -- that both represents a DOM tree and can +To start with, we need to create a data-structure – let's call it +JavaScript Markup (JSM) – that both represents a DOM tree and can also be used to insert one into the browser's DOM. And to do that we need to understand what a tree of DOM nodes is constructed of. What parts do you see here? @@ -227,15 +226,15 @@ This is what I'm thinking: As you can see, we have a clear mapping from our notation, JSM, to the original HTML. Our tree is made up of three element arrays. The first item in the array is the tag, the second is an object containing the -tag's properties, and the third is an array of its children; which are +tag's properties, and the third is an array of its children which are all made up of the same three element arrays. -The truth is though, if you stare at it long enough, although the +The truth is, if you stare at it long enough, although the mapping is clear, how much fun would it be to read and write that on a -consistent basis? I can assure you, it is rather not fun. But it has +consistent basis? I can assure you, it is not fun. But it has the advantage of being easy to insert into the DOM. All you need to do is write a simple recursive function that ingests our data structure -and updates the DOM accordingly. We will get back to that. +and updates the DOM accordingly. We’ll get back to that. So now we have a way to represent a tree of nodes and we (theoretically) have a way to get those nodes into the DOM. But if we @@ -246,7 +245,7 @@ And this is where our object of study enters the scene. JSX is just a notation that a compiler takes as input and outputs in its place a tree of nodes nearly identical to the notation we came up with! And if you look back to our notation you can see that you can easily embed -arbitrary JavaScript expressions wherever you want in a node. As you +in a node arbitrary JavaScript expressions wherever you want. As you may have realized, that's exactly what the JSX compiler does when it sees curly braces! @@ -270,12 +269,12 @@ React.createElement( ); #+END_SRC -As you can see, it is very similar to our JSM data-structure and for -the purposes of this book we will use JSM, as it's a bit easier to +As you can see, it is very similar to our JSM data-structure and, for +the purposes of this book, we’ll use JSM, as it's a bit easier to work with. A JSX compiler also does some validation and escapes input to prevent cross-site scripting attacks. In practice though, it would -behave the same in our areas of study and we will keep things simple -by leaving those aspects of the JSX compiler out. +behave the same in our areas of study and we’ll keep things simple +by leaving out those aspects of the JSX compiler. So now that we've worked through JSX we're ready to tackle ~createElement~, the next item on our way to building our own React. @@ -304,10 +303,10 @@ React expects nodes defined as JavaScript objects that look like this: } #+END_SRC -That is: an object with two properties: ~type~ and ~props~. The +That is, an object with two properties: ~type~ and ~props~. The ~props~ property contains all the properties of the node. The node's ~children~ are also considered part of its properties. The full -version of React's ~createElement~ includes more properties but they +version of React's ~createElement~ includes more properties, but they are not relevant to our study here. #+BEGIN_SRC javascript @@ -340,7 +339,7 @@ elements. The first part tests whether ~node~ is a complex node (specified by an array) and then generates an ~element~ object based on the input node. It recursively calls ~createElement~ to generate an array of children elements. If the node is not complex then we -generate an element of type 'TEXT' which we use for all primitives +generate an element of type 'TEXT' which we use for all primitives, like strings and numbers. We call the output of ~createElement~ a tree of ~elements~ (surprise). @@ -353,9 +352,8 @@ process of rendering our tree to the DOM! :END: 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. +own React. The next piece is ~render~. How do we go from our JSM tree +of nodes to actually displaying something on screen? We do this by exploring the ~render~ method. The signature for our ~render~ method should be familiar to you: @@ -375,16 +373,16 @@ function render(element, container) { #+END_SRC Our DOM element is created first. Then we set the properties, render -children elements, and finally append the whole tree to the +the children elements, and finally append the whole tree to the container. -Now that we have an idea of what to build we will work on expanding +Now that we have an idea of what to build we’ll work on expanding the pseudocode until we have our own fully functional ~render~ method -using the same general algorithm React uses. In our first pass we will +by using the same general algorithm that React uses. In our first pass we’ll focus on the initial render and ignore reconciliation. #+begin_note -Reconciliation is basically React's "diffing" algorithm. We will be +Reconciliation is basically React's "diffing" algorithm. We’ll be exploring it after we work out the initial render. #+end_note @@ -428,7 +426,7 @@ reconciliation. :PROPERTIES: :EXPORT_FILE_NAME: manuscript/reconciliation.markua :END: -A tale of two trees. These are the two trees that people most often +This is a tale of two trees, 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 @@ -444,48 +442,48 @@ diffing" algorithm. Unfortunately, those researching tree diffing in Computer Science have not yet produced a generic algorithm with sufficient performance for -use in something like React; as the current best algorithm still [[https://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf][runs +use in something like React, as the current best algorithm still [[https://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf][runs in O(n^3)]]. Since an O(n^3) algorithm isn't going to cut it in the real-world, the creators of React instead use a set of heuristics to determine what -parts of the tree have changed. Understanding how the React tree -diffing algorithm works in general and the heuristics currently in use -can help immensely in detecting and fixing React performance +parts of the tree have changed. Understanding the heuristics currently +in use and how the React tree diffing algorithm works in general can +help immensely in detecting and fixing React performance bottlenecks. And beyond that it can help one's understanding of some of React's quirks and usage. Even though this algorithm is internal to -React and can be changed anytime its details have leaked out in some -ways and are overall unlikely to change in major ways without larger +React and can be changed anytime, its details have leaked out in some +ways and, overall, are unlikely to change in major ways without larger changes to React itself. -According to the [[https://reactjs.org/docs/reconciliation.html][React documentation]] their diffing algorithm is O(n) -and based on two major components: +According to the [[https://reactjs.org/docs/reconciliation.html][React documentation]] the diffing algorithm is O(n) +and is based on two major components: - Elements of differing types will yield different trees - You can hint at tree changes with the ~key~ prop. -In this section we will focus on the first part: differing types. +In this section we’ll focus on the first part: differing types. #+begin_note -In this book we won't be covering keys in depth but you will see why +In this book we won't be covering keys in depth, but you’ll see why it's very important to follow the guidance from React's documentation -that keys be: stable, predictable, and unique. +that keys are stable, predictable, and unique. #+end_note -The approach we will take here is to integrate the heuristics that +The approach we’ll take here is to integrate the heuristics that React uses into our ~render~ method. Our implementation will be very -similar to how React itself does it and we will discuss React's actual +similar to how React itself does it and we’ll discuss React's actual implementation later when we talk about Fibers. -Before we get into the code changes that implement the heuristics it +Before we get into the code changes that implement the heuristics, it is important to remember that React /only/ looks at an element's type, existence, and key. It does not do any other diffing. It does not diff props. It does not diff sub-trees of modified parents. -While keeping that in mind, here is an overview of the algorithm we -will be implementing in the ~render~ method. ~element~ is the element -from the current tree and ~prevElement~ is the corresponding element -in the tree from the previous render. +While keeping that in mind, here is an overview of the algorithm we’ll +be implementing in the ~render~ method. ~element~ is the element from +the current tree and ~prevElement~ is the corresponding element in the +tree from the previous render. #+BEGIN_SRC javascript if (!element && prevElement) @@ -505,21 +503,21 @@ still be invoked. 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 +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 reuse 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. +objects will have the same structure as our current elements but we’ll +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~. ~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 +of elements at a time, so it works well to use it as a key for ~renderTrees~. #+BEGIN_SRC javascript @@ -537,16 +535,16 @@ As you can see, the change we made is to move the core of our algorithm into a new function called ~render_internal~ and pass in the result of our last render to ~render_internal~. -Now that we have stored our last render tree we can go ahead and +Now that we have stored our last render tree, we can go ahead and update our render method with the heuristics for reusing the DOM elements. We name it ~render_internal~ because it is what controls the -rendering but takes an additional argument now: the +rendering, but it now takes an additional argument: the ~prevElement~. ~prevElement~ is a reference to the corresponding ~element~ from the previous render and contains a reference to its associated DOM element and parent DOM element. If it's the first -render or if we are rendering a new node or branch of the tree then +render or if we are rendering a new node or branch of the tree, then ~prevElement~ will be ~undefined~. If, however, ~element~ is -~undefined~ and ~prevElement~ is defined then we know we need to +~undefined~ and ~prevElement~ is defined, then we know we need to delete a node that previously existed. #+BEGIN_SRC javascript @@ -589,11 +587,11 @@ render its children is when we are deleting an existing DOM element. We use this observation to group the calls for ~setDOMProps~ and ~renderChildren~. Choosing when to append a new DOM element to the container is also part of the heuristics. If we can reuse an existing -DOM element then we do, but if the element type has changed or if -there was no corresponding existing DOM element then and only then do +DOM element, then we do this, but if the element type has changed or if +there was no corresponding existing DOM element, then, and only then, do we append a new DOM element. This ensures the actual DOM tree isn't being replaced every time we render, only the elements that change are -replaced. +being replaced. In the real React, when a new DOM element is appended to the DOM tree, React would invoke ~componentDidMount~ or schedule ~useEffect~. @@ -612,12 +610,12 @@ function removeDOMElement(prevElement) { } #+END_SRC -In creating a new DOM element we just need to branch if we are +In creating a new DOM element, we just need to branch if we are creating a text element since the browser API differs slightly. We -also populate the text element's value as the API requires the first -argument to be specified even though later on when we set props we -will set it again. This is where React would invoke -~componentWillMount~ or schedule ~useEffect~. +also populate the text element's value, as the API requires the first +argument to be specified even though later on when we set props we’ll +set it again. This is where React would invoke ~componentWillMount~ or +schedule ~useEffect~. #+BEGIN_SRC javascript function createDOMElement(element) { @@ -656,7 +654,7 @@ need to be updated or removed. #+begin_warning This algorithm for setting props does not correctly handle events, -which must be treated specially. For this exercise that detail is not +which must be treated specially. For this exercise, that detail is not important and we leave it out for simplicity. #+end_warning @@ -665,7 +663,7 @@ elements that are no longer being used. This would happen when the number of children is decreased. The second loop starts at the first child and then iterates through all of the children of the parent element, calling ~render_internal~ on each child. When -~render_internal~ is called the corresponding previous element in that +~render_internal~ is called, the corresponding previous element in that position is passed to ~render_internal~, or ~undefined~ if there is no corresponding element, like when the list of children has grown. @@ -692,7 +690,7 @@ It's very important to understand the algorithm used here because this is essentially what happens in React when incorrect keys are used, like using a list index for a key. And this is why keys are so critical to high performance (and correct) React code. For example, in -our algorithm here, if you removed an item from the front of the list +our algorithm here, if you removed an item from the front of the list, you may cause every element in the list to be created anew in the DOM if the types no longer match up. In this book we won't be incorporating keys, but it's actually only a minor difference in @@ -700,7 +698,7 @@ determining which ~child~ gets paired with which ~prevChild~. Otherwise this is effectively the same algorithm React uses when rendering lists of children. -#+CAPTION: Example of ~renderChildren~ 2nd loop when the 1st element has been removed. In this case the trees for all of the children will be torn down and rebuilt. +#+CAPTION: Example of ~renderChildren~ 2nd loop when the 1st element has been removed. In this case, the trees for all of the children will be torn down and rebuilt. | i | child Type | prevChild Type | |---+------------+----------------| | 0 | span | div | @@ -709,8 +707,8 @@ uses when rendering lists of children. There are a few things to note here. First, it is important to pay attention to when React will be removing a DOM element from the tree -and adding a new one as this is when the related lifecycle events or -hooks are invoked. And invoking those lifecycle methods or hooks, and +and adding a new one, as this is when the related lifecycle events, or +hooks, are invoked. And invoking those lifecycle methods, or hooks, and the whole process of tearing down and building up a component is expensive. So again, if you use a bad key, like the algorithm here simulates, you'll be hitting a major performance bottleneck since @@ -724,24 +722,24 @@ tearing down and rebuilding the trees of child components. 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 +with the introduction of Fibers. Fibers is 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 +and resume again. This is the underlying change that enables the +experimental Concurrent Mode as well as runs most hooks without blocking the render. -But even with such a large change, the underlying algorithms for -deciding how and when to render components is the same. And when not -running in Concurrent Mode the effect is still the same as React does -the render phase in one block still. So using a simplified +But even with such a large change, the underlying algorithms that +decide how and when to render components are the same. And, when not +running in Concurrent Mode, the effect is still the same, as React +still does the render phase in one block. So, using a simplified interpretation that doesn't include all the complexities of breaking up the process in to chunks enables us to see more clearly how the -process as a whole works. At this point bottlenecks are much more -likely to occur from the underlying algorithms and not from the Fiber +process works as a whole. At this point, bottlenecks are much more +likely to occur from the underlying algorithms and not from the Fibers specific details. * Putting it all together @@ -749,7 +747,7 @@ specific details. :EXPORT_FILE_NAME: manuscript/putting-it-all-together.markua :END: -Now that we've explored how React renders your components it's time to +Now that we've explored how React renders your components, it's time to finally create some components and use them! #+BEGIN_SRC javascript @@ -771,7 +769,7 @@ render(createElement(App()), document.getElementById('root')); #+END_SRC -We are creating two components, that output JSM, as we defined it +We are creating two components that output JSM, as we defined it earlier. We create one component prop for the ~SayNow~ component: ~dateTime~. It gets passed from the ~App~ component. The ~SayNow~ component prints out the ~DateTime~ passed in to it. You might notice @@ -787,17 +785,17 @@ setInterval(() => 1000); #+END_SRC -If you run the code above you will see the DateTime display being -updated every second. And if you watch in your dev tools or if you -profile the run you will see that the only part of the DOM that gets +If you run the code above you’ll see the DateTime display being +updated every second. And if you watch in your dev tools, or if you +profile the run, you’ll see that the only part of the DOM that gets updated or replaced is the part that changes (aside from the DOM props). We now have a working version of our own React. #+begin_note This implementation is designed for teaching purposes and has some -known issues and bugs, like always updating the DOM props, along with -other things. Fundamentally, it functions the same as React but if you -wanted to use it in a more production setting it would take a lot more +known bugs, like always updating the DOM props, along with +other issues. Fundamentally, it functions the same as React, but if you +want to use it in a more production-like setting, it would take a lot more development. #+end_note @@ -808,7 +806,7 @@ development. Of course our version of React elides over many details that React must contend with, like starting a re-render from where state changes -and event handlers. For understanding how to build high-performance +and event handlers. To build high-performance React applications, however, the most important piece to understand is how and when React renders components, which is what we have learned in creating our own mini version of React. @@ -820,11 +818,11 @@ components, and how React chooses when to replace a node or re-use one. If your React application is performing poorly you can think about which part of the algorithm or heuristics might be the issue. -Now, there is a lot more to explore. Like how do you track down the -cause of a performance bottleneck? Or how do you use the React APIs in +Now, there is a lot more to explore. Like, how do you track down the +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. +down and understand with the foundations we covered. So I hope this is +just the start of your high-performance React journey. * Image Test :noexport: :PROPERTIES: :EXPORT_FILE_NAME: manuscript/image-test.markua