Edits to the first chapter.
This commit is contained in:
@@ -56,7 +56,7 @@ the method will remain the same.
|
||||
|
||||
TODO note that the book references React-DOM but the algorithms should
|
||||
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
|
||||
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
|
||||
@@ -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
|
||||
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
|
||||
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 tree.
|
||||
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
|
||||
@@ -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
|
||||
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
|
||||
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.
|
||||
|
||||
React is made up of a few pieces: ~createElement~, ~render~, and
|
||||
reconciliation. The first building block is ~createElement~. While
|
||||
~createElement~ is itself unlikely to be a 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 in our mental model the
|
||||
harder it will be for us to diagnose performance problems.
|
||||
React is centered around 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 me have to understand its input. The input comes from
|
||||
~createElement~. While ~createElement~ itself is unlikely to be a
|
||||
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
|
||||
in our mental model the harder it will be for us to diagnose
|
||||
performance problems.
|
||||
|
||||
** ~JSX~
|
||||
** Markup in Javascript: ~JSX~
|
||||
|
||||
But before we get to ~createElement~ we should talk about JSX. 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.
|
||||
~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
|
||||
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
|
||||
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
|
||||
<div class="header">
|
||||
<h1>Hello</h1>
|
||||
@@ -159,7 +158,15 @@ probably do after what we've already done
|
||||
#+END_SRC
|
||||
|
||||
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
|
||||
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
|
||||
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 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
|
||||
sees curly braces!
|
||||
|
||||
There are three main differences between our data structure and the
|
||||
real one that JSX compiler outputs: it uses objects instead of arrays,
|
||||
it inserts calls to React.createElement on children, and spreads the
|
||||
children instead of containing them in an array. Here is what "real"
|
||||
JSX compiler output looks like:
|
||||
real one that the JSX compiler outputs: it uses objects instead of
|
||||
arrays, it inserts calls to React.createElement on children, and
|
||||
spreads the children instead of containing them in an array. Here is
|
||||
what "real" JSX compiler output looks like:
|
||||
|
||||
#+BEGIN_SRC javascript
|
||||
React.createElement(
|
||||
@@ -221,15 +228,21 @@ React.createElement(
|
||||
|
||||
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
|
||||
it's a bit easier to work with. In practice they would behave the same
|
||||
in the ways that matter to us now.
|
||||
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 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
|
||||
~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:
|
||||
|
||||
@@ -245,20 +258,12 @@ React expects nodes defined as Javascript objects that look like this:
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
That is an object with two properties: ~type~ and ~props~. The ~props~
|
||||
property contains all the properties of the node. The node's
|
||||
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
|
||||
React's ~createElement~ includes more properties but they are unlikely
|
||||
to be relevant to your application's performance or our 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.
|
||||
version of React's ~createElement~ includes more properties but they
|
||||
are unlikely to be relevant to your application's performance or our
|
||||
version of React here.
|
||||
|
||||
#+BEGIN_SRC javascript
|
||||
function createElement(node) {
|
||||
@@ -300,11 +305,10 @@ process of rendering our tree to the DOM!
|
||||
** Render
|
||||
|
||||
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?
|
||||
|
||||
The signature for our ~render~ method is very simple and will be
|
||||
familiar to you:
|
||||
The signature for our ~render~ method should be familiar to you:
|
||||
|
||||
#+BEGIN_SRC javascript
|
||||
function render(element, container)
|
||||
@@ -321,7 +325,12 @@ function render(element, container) {
|
||||
container.appendChild(domElement);
|
||||
#+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
|
||||
function render(element, container) {
|
||||
@@ -347,20 +356,17 @@ function render(element, container) {
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
Next, we look at renderDOMElement which must also set properties on
|
||||
the newly created DOM element and render any children.
|
||||
|
||||
To start with we create the DOM element. Then we need to set its
|
||||
We begin by creating the DOM element. Then we need to set its
|
||||
properties. To do this we first need to filter out the ~children~
|
||||
property and then we simply loop over they keys setting each property
|
||||
directly. Then we render each of the children by looping over the
|
||||
children recursively calling ~render~ on each with the ~container~ set
|
||||
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
|
||||
DOM! But so far we can only add things to our tree. To be able to
|
||||
remove and modify the tree we need two more parts: reconciliation and
|
||||
the commit phase.
|
||||
Now we can go all the way from our JSX-like notation to a rendered
|
||||
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
|
||||
A tale of two trees. These are the two trees that people most often
|
||||
@@ -455,11 +461,11 @@ function render(element, container) {
|
||||
}
|
||||
#+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
|
||||
ahead and update our render method with the heuristics.
|
||||
|
||||
TODO note that we are adding parent and domElement properties.
|
||||
|
||||
#+BEGIN_SRC javascript
|
||||
function render_internal(element, container, prevElement) {
|
||||
let domElement, children;
|
||||
|
||||
Reference in New Issue
Block a user