From ecb1d96dc811634601f69cfd576b4a9c48d83258 Mon Sep 17 00:00:00 2001 From: Thomas Hintz Date: Sat, 25 Jul 2020 07:50:20 -0700 Subject: [PATCH] Adding createElement. --- high-performance-react.org | 73 ++++++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/high-performance-react.org b/high-performance-react.org index a327c35..dec29cf 100644 --- a/high-performance-react.org +++ b/high-performance-react.org @@ -115,7 +115,7 @@ 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~ +** ~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 @@ -226,25 +226,75 @@ So now that we've worked through JSX we're ready to tackle TODO JSX also does validation and escapes input to prevent XXS -*** ~createElement~ +** ~createElement~ -A tale of two trees. +React expects nodes defined as Javascript objects that look like this: + +#+BEGIN_SRC javascript +{ + type: NODE_TYPE, + props: { + propA: VALUE, + propB: VALUE, + ... + children: STRING | ARRAY + } +} +#+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 +~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. #+BEGIN_SRC javascript function createElement(node) { - if (typeof node === 'string') { + // an array: not text, number, or other primitive + if (typeof node === 'object') { + const [ tag, props, children ] = node; return { - type: 'TEXT_ELEMENT' - } - const [ tag, props, children ] = node; - const element = document.createDOMElement(tag); - for ([key, val] in props) { - element[key] = val; + type: tag, + props: { + ...props, + children: children.map(createElement) + } + }; } - children.forEach(createElement); + + // primitives like text or number + return { + type: 'TEXT', + props: { + nodeValue: node, + children: [] + } + }; } #+END_SRC +Our ~createElement~ has two main parts: complex elements and primitive +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 +like strings and numbers. + +That's it. Now we have everything we need to actually begin the +process of rendering our tree to the DOM! + +*** Reconciliation +A tale of two trees. * Rendering Model React calls shouldComponentUpdate to know if it should re-render the component. by default it returns true. @@ -267,6 +317,7 @@ function createElement(node) { * Concurrent Rendering * UX * JS Service Workers +* Keys * Reconciliation - diffing algorithm based on heuristics. generic algorithm is O(n^3) - "Fiber" algorithm notes