Edits to the first chapter.

master
Thomas Hintz 4 years ago
parent 66faaa4efe
commit 2dd5e879af

@ -56,7 +56,7 @@ the method will remain the same.
TODO note that the book references React-DOM but the algorithms should TODO note that the book references React-DOM but the algorithms should
generally apply to all React implementations. generally apply to all React implementations.
* Mini React * Fundamentals: Building our own React
Baking bread. When I first began to learn how to bake bread the recipe 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 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 combine them and prescribed times of rest. It gave me an oven
@ -93,13 +93,13 @@ 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 consistently would fall out-of-date. So like the bread, to produce consistently
good results we need to understand how React does what it does. good results we need to understand how React does what it does.
** Basic React ** React, made of
Conceptually React is very simple. It starts by walking a tree of Conceptually React is very simple. It starts by walking a tree of
components and building up a tree of their output. Then it compares 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 that tree to the tree currently in the browser's DOM to find any
differences between them. When it finds differences it updates the differences between them. When it finds differences it updates the
browser's DOM to match its tree. browser's DOM to match its internal tree.
But what does that actually look like? If your app is janky does that 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 explanation point you towards what is wrong? No. It might make you
@ -108,22 +108,31 @@ 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 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 making bread worked. It's time we build up our
understand of how React works so that we can start to answer our understanding of how React works so that we can start to answer our
questions with solid answers. questions with solid answers.
React is made up of a few pieces: ~createElement~, ~render~, and React is centered around the ~render~ method. The ~render~ method is
reconciliation. The first building block is ~createElement~. While what walks our trees, diffs them with the browser's DOM tree, and
~createElement~ is itself unlikely to be a bottleneck it's a good to updates the DOM as needed. But before we can look at the ~render~
understand how it works so that we can have a complete picture of the method me have to understand its input. The input comes from
entire process. The more black-boxes we have in our mental model the ~createElement~. While ~createElement~ itself is unlikely to be a
harder it will be for us to diagnose performance problems. bottleneck it's a good to understand how it works so that we can have
a complete picture of the entire process. The more black-boxes we have
** ~JSX~ in our mental model the harder it will be for us to diagnose
performance problems.
But before we get to ~createElement~ we should talk about JSX. While
not strictly a part of React it is almost universally used with ** Markup in Javascript: ~JSX~
it. And if we understand it then ~createElement~ will be less of a
mystery since we will be able to connect all the dots. ~createElement~, however, takes as input something that is probably
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.
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 the normal way of injecting HTML into the DOM was via Before JSX the normal way of injecting HTML into the DOM was via
directly utilizing the browser's DOM APIs. This was very cumbersome. directly utilizing the browser's DOM APIs. This was very cumbersome.
@ -141,16 +150,6 @@ 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 DOM. And to do that we need to understand what a tree of DOM nodes is
constructed of. What parts do you see here? constructed of. What parts do you see here?
TODO include text element (Hello)
TODO change to using objects instead of arrays?
probably do after what we've already done
{
type: 'h1',
props: { x: y },
children: []
}
#+BEGIN_SRC html #+BEGIN_SRC html
<div class="header"> <div class="header">
<h1>Hello</h1> <h1>Hello</h1>
@ -159,7 +158,15 @@ probably do after what we've already done
#+END_SRC #+END_SRC
I see three parts: the name of the tag, the tag's properties, and its I see three parts: the name of the tag, the tag's properties, and its
children. Now how could we recreate that in Javascript? children.
#+BEGIN_SRC
tag name: 'div'
tag prop: 'class'
children: h1..., 'Hello', input...
#+END_SRC
Now how could we recreate that in Javascript?
In Javascript we store lists of things in arrays and key/value In Javascript we store lists of things in arrays and key/value
properties in objects. Luckily for us Javascript even gives us literal properties in objects. Luckily for us Javascript even gives us literal
@ -198,15 +205,15 @@ 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 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 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 you look back to our notation you can see that you can easily embed
arbitrary Javascript expression wherever you want in a node. As you arbitrary Javascript expressions wherever you want in a node. As you
may have realized, that's exactly what the JSX compiler does when it may have realized, that's exactly what the JSX compiler does when it
sees curly braces! sees curly braces!
There are three main differences between our data structure and the There are three main differences between our data structure and the
real one that JSX compiler outputs: it uses objects instead of arrays, real one that the JSX compiler outputs: it uses objects instead of
it inserts calls to React.createElement on children, and spreads the arrays, it inserts calls to React.createElement on children, and
children instead of containing them in an array. Here is what "real" spreads the children instead of containing them in an array. Here is
JSX compiler output looks like: what "real" JSX compiler output looks like:
#+BEGIN_SRC javascript #+BEGIN_SRC javascript
React.createElement( React.createElement(
@ -221,15 +228,21 @@ React.createElement(
As you can see it is very similar to our data-structure and for the As you can see it is very similar to our data-structure and for the
purposes of this book we will use our own simplified data-structure as purposes of this book we will use our own simplified data-structure as
it's a bit easier to work with. In practice they would behave the same it's a bit easier to work with. A JSX compiler also does some
in the ways that matter to us now. validation and escapes input to prevent cross-site scripting
attacks. In practice though they would behave the same in the ways
that matter to us now.
So now that we've worked through JSX we're ready to tackle So now that we've worked through JSX we're ready to tackle
~createElement~, the item on our way to building our own React. ~createElement~, the next item on our way to building our own React.
TODO JSX also does validation and escapes input to prevent XXS ** Getting Ready to Render with ~createElement~
** ~createElement~ React's ~render~ expects to consume a tree of element objects in a
specific, uniform format. ~createElement~ is the method by which we
achieve that objective. ~createElement~ will take as input our
JSX-like notation and output a tree of objects compatible with
~render~.
React expects nodes defined as Javascript objects that look like this: React expects nodes defined as Javascript objects that look like this:
@ -245,20 +258,12 @@ React expects nodes defined as Javascript objects that look like this:
} }
#+END_SRC #+END_SRC
That is an object with two properties: ~type~ and ~props~. The ~props~ That is: an object with two properties: ~type~ and ~props~. The
property contains all the properties of the node. The node's ~props~ property contains all the properties of the node. The node's
~children~ are also considered part of its properties. The full ~children~ are also considered part of its properties. The full
React's ~createElement~ includes more properties but they are unlikely version of React's ~createElement~ includes more properties but they
to be relevant to your application's performance or our version of are unlikely to be relevant to your application's performance or our
React here. version of React here.
#+BEGIN_SRC javascript
// React's createElement
const ReactElement = function(type, key, ref, self, source, owner, props)
#+END_SRC
So all our ~createElement~ needs to do is transform our data structure
into the objects that our React expects.
#+BEGIN_SRC javascript #+BEGIN_SRC javascript
function createElement(node) { function createElement(node) {
@ -300,11 +305,10 @@ process of rendering our tree to the DOM!
** Render ** Render
There are now only two major puzzles remaining in our quest for our 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 tree of own React. The next piece is: ~render~. How do we go from our tree of
nodes to actually displaying something on screen? nodes to actually displaying something on screen?
The signature for our ~render~ method is very simple and will be The signature for our ~render~ method should be familiar to you:
familiar to you:
#+BEGIN_SRC javascript #+BEGIN_SRC javascript
function render(element, container) function render(element, container)
@ -321,7 +325,12 @@ function render(element, container) {
container.appendChild(domElement); container.appendChild(domElement);
#+END_SRC #+END_SRC
TODO and now full code: Our DOM element is created first. Then we set the properties, render
children elements, and finally append the whole tree to the
container. Now we will work on expanding the psuedocode until we build
our own fully functional ~render~ method using the same general
algorithm React uses. Next we will focus on the initial render and
ignore reconciliation.
#+BEGIN_SRC javascript #+BEGIN_SRC javascript
function render(element, container) { function render(element, container) {
@ -347,20 +356,17 @@ function render(element, container) {
} }
#+END_SRC #+END_SRC
Next, we look at renderDOMElement which must also set properties on We begin by creating the DOM element. Then we need to set its
the newly created DOM element and render any children.
To start with we create the DOM element. Then we need to set its
properties. To do this we first need to filter out the ~children~ properties. To do this we first need to filter out the ~children~
property and then we simply loop over they keys setting each property property and then we simply loop over they keys setting each property
directly. Then we render each of the children by looping over the directly. Then we render each of the children by looping over the
children recursively calling ~render~ on each with the ~container~ set children recursively calling ~render~ on each with the ~container~ set
to the current DOM element (which is each child's parent). to the current DOM element (which is each child's parent).
Now we can go all the way from JSX to a rendered tree in the browser's Now we can go all the way from our JSX-like notation to a rendered
DOM! But so far we can only add things to our tree. To be able to tree in the browser's DOM! But so far we can only add things to our
remove and modify the tree we need two more parts: reconciliation and tree. To be able to remove and modify the tree we need one more part:
the commit phase. reconciliation.
** Reconciliation ** Reconciliation
A tale of two trees. These are the two trees that people most often A tale of two trees. These are the two trees that people most often
@ -455,11 +461,11 @@ function render(element, container) {
} }
#+END_SRC #+END_SRC
TODO note that we are adding parent and domElement properties.
Now that we have a way to see what we rendered last time we can go Now that we have a way to see what we rendered last time we can go
ahead and update our render method with the heuristics. ahead and update our render method with the heuristics.
TODO note that we are adding parent and domElement properties.
#+BEGIN_SRC javascript #+BEGIN_SRC javascript
function render_internal(element, container, prevElement) { function render_internal(element, container, prevElement) {
let domElement, children; let domElement, children;

Loading…
Cancel
Save