Formatting updates.

master
Thomas Hintz 4 years ago
parent b470727093
commit ba4f38c5a8

@ -12,6 +12,7 @@
#+TITLE: Foundations of High-Performance React Applications
#+AUTHOR: Thomas Hintz
#+EXCLUDE_TAGS: noexport
#+startup: indent
#+tags: noexport sample frontmatter mainmatter backmatter
@ -196,7 +197,8 @@ This is what I'm thinking:
#+BEGIN_SRC javascript
['div', { 'className': 'header' },
[['h1', {}, ['Hello']],
['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
['input', { 'type': 'submit', 'disabled': 'disabled' },
[]]
]
]
#+END_SRC
@ -345,10 +347,10 @@ focusing on the initial render. In pseudocode it looks like this:
#+BEGIN_SRC javascript
function render(element, container) {
const domElement = createDOMElement(element);
setProps(element, domElement);
renderChildren(element, domElement);
container.appendChild(domElement);
const domElement = createDOMElement(element);
setProps(element, domElement);
renderChildren(element, domElement);
container.appendChild(domElement);
#+END_SRC
Our DOM element is created first. Then we set the properties, render
@ -367,23 +369,24 @@ exploring it after we work out the initial render.
#+BEGIN_SRC javascript
function render(element, container) {
const { type, props } = element;
const { type, props } = element;
// create the DOM element
const domElement = type === 'TEXT' ?
document.createTextNode(props.nodeValue) :
document.createElement(type);
// create the DOM element
const domElement = type === 'TEXT' ?
document.createTextNode(props.nodeValue) :
document.createElement(type);
// set its properties
Object.keys(props)
.filter((key) => key !== 'children')
.forEach((key) => domElement[key] = props[key]);
// set its properties
Object.keys(props)
.filter((key) => key !== 'children')
.forEach((key) => domElement[key] = props[key]);
// render its children
props.children.forEach((child) => render(child, domElement));
// render its children
props.children.forEach((child) =>
render(child, domElement));
// add our tree to the DOM!
container.appendChild(domElement);
// add our tree to the DOM!
container.appendChild(domElement);
}
#+END_SRC
@ -464,14 +467,14 @@ from the current tree and ~prevElement~ is the corresponding element
in the tree from the previous render.
#+BEGIN_SRC javascript
if (!element && prevElement)
// delete dom element
else if (element && !prevElement)
// add new dom element, render children
else if (element.type === prevElement.type)
// update dom element, render children
else if (element.type !== prevElement.type)
// replace dom element, render children
if (!element && prevElement)
// delete dom element
else if (element && !prevElement)
// add new dom element, render children
else if (element.type === prevElement.type)
// update dom element, render children
else if (element.type !== prevElement.type)
// replace dom element, render children
#+END_SRC
Notice that in every case, except deletion, we still call ~render~ on
@ -497,10 +500,10 @@ tree, keyed by the ~container~.
#+BEGIN_SRC javascript
const renderTrees = {};
function render(element, container) {
const tree =
render_internal(element, container, renderTrees[container]);
// render complete, store the updated tree
renderTrees[container] = tree;
const tree =
render_internal(element, container, renderTrees[container]);
// render complete, store the updated tree
renderTrees[container] = tree;
}
#+END_SRC
@ -522,34 +525,34 @@ delete a node that previously existed.
#+BEGIN_SRC javascript
function render_internal(element, container, prevElement) {
let domElement, children;
if (!element && prevElement) {
removeDOMElement(prevElement);
return;
} else if (element && !prevElement) {
domElement = createDOMElement(element);
} else if (element.type === prevElement.type) {
domElement = prevElement.domElement;
} else { // types don't match
removeDOMElement(prevElement);
domElement = createDOMElement(element);
}
setDOMProps(element, domElement, prevElement);
children = renderChildren(element, domElement, prevElement);
if (!prevElement || domElement !== prevElement.domElement) {
container.appendChild(domElement);
}
return {
domElement: domElement,
parent: container,
type: element.type,
props: {
...element.props,
children: children
}
};
let domElement, children;
if (!element && prevElement) {
removeDOMElement(prevElement);
return;
} else if (element && !prevElement) {
domElement = createDOMElement(element);
} else if (element.type === prevElement.type) {
domElement = prevElement.domElement;
} else { // types don't match
removeDOMElement(prevElement);
domElement = createDOMElement(element);
}
setDOMProps(element, domElement, prevElement);
children = renderChildren(element, domElement, prevElement);
if (!prevElement || domElement !== prevElement.domElement) {
container.appendChild(domElement);
}
return {
domElement: domElement,
parent: container,
type: element.type,
props: {
...element.props,
children: children
}
};
}
#+END_SRC
@ -577,7 +580,7 @@ the parent element. Before removing the element, React would invoke
#+BEGIN_SRC javascript
function removeDOMElement(prevElement) {
prevElement.parent.removeChild(prevElement.domElement);
prevElement.parent.removeChild(prevElement.domElement);
}
#+END_SRC
@ -590,9 +593,9 @@ will set it again. This is where React would invoke
#+BEGIN_SRC javascript
function createDOMElement(element) {
return element.type === 'TEXT' ?
document.createTextNode(element.props.nodeValue) :
document.createElement(element.type);
return element.type === 'TEXT' ?
document.createTextNode(element.props.nodeValue) :
document.createElement(element.type);
}
#+END_SRC
@ -603,18 +606,18 @@ and it isn't intended to be set directly.
#+BEGIN_SRC javascript
function setDOMProps(element, domElement, prevElement) {
if (prevElement) {
Object.keys(prevElement.props)
.filter((key) => key !== 'children')
.forEach((key) => {
domElement[key] = ''; // clear prop
});
}
Object.keys(element.props)
.filter((key) => key !== 'children')
.forEach((key) => {
domElement[key] = element.props[key];
});
if (prevElement) {
Object.keys(prevElement.props)
.filter((key) => key !== 'children')
.forEach((key) => {
domElement[key] = ''; // clear prop
});
}
Object.keys(element.props)
.filter((key) => key !== 'children')
.forEach((key) => {
domElement[key] = element.props[key];
});
}
#+END_SRC
@ -640,17 +643,17 @@ corresponding element, like when the list of children has grown.
#+BEGIN_SRC javascript
function renderChildren(element, domElement, prevElement = { props: { children: [] }}) {
const elementLen = element.props.children.length;
const prevElementLen = prevElement.props.children.length;
// remove now unused elements
for (let i = elementLen; i < prevElementLen - elementLen; i++) {
removeDOMElement(element.props.children[i]);
}
// render existing and new elements
return element.props.children.map((child, i) => {
const prevChild = i < prevElementLen ? prevElement.props.children[i] : undefined;
return render_internal(child, domElement, prevChild);
});
const elementLen = element.props.children.length;
const prevElementLen = prevElement.props.children.length;
// remove now unused elements
for (let i = elementLen; i < prevElementLen - elementLen; i++) {
removeDOMElement(element.props.children[i]);
}
// render existing and new elements
return element.props.children.map((child, i) => {
const prevChild = i < prevElementLen ? prevElement.props.children[i] : undefined;
return render_internal(child, domElement, prevChild);
});
}
#+END_SRC
@ -719,19 +722,19 @@ At this point the only thing left to do is to create some components
and use them!
#+BEGIN_SRC javascript
const SayNow = ({ dateTime }) => {
return ['h1', {}, [`It is: ${dateTime}`]];
};
const App = () => {
return ['div', { 'className': 'header' },
[SayNow({ dateTime: new Date() }),
['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
]
];
}
const SayNow = ({ dateTime }) => {
return ['h1', {}, [`It is: ${dateTime}`]];
};
const App = () => {
return ['div', { 'className': 'header' },
[SayNow({ dateTime: new Date() }),
['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
]
];
}
render(createElement(App()), document.getElementById('root'));
render(createElement(App()), document.getElementById('root'));
#+END_SRC
We are creating two components, that output JSM, as we defined it
@ -745,8 +748,8 @@ The next step is to call render multiple times.
#+BEGIN_SRC javascript
setInterval(() =>
render(createElement(App()), document.getElementById('root')),
1000);
render(createElement(App()), document.getElementById('root')),
1000);
#+END_SRC
If you run the code above you will see the DateTime display being
@ -787,7 +790,7 @@ 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.
* Image Test
* Image Test :noexport:
:PROPERTIES:
:EXPORT_FILE_NAME: manuscript/image-test.markua
:END:

@ -10,4 +10,3 @@ reconciliation.markua
fibers.markua
putting-it-all-together.markua
conclusion.markua
image-test.markua

@ -34,7 +34,8 @@ This is what I'm thinking:
```
['div', { 'className': 'header' },
[['h1', {}, ['Hello']],
['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
['input', { 'type': 'submit', 'disabled': 'disabled' },
[]]
]
]
```

@ -5,15 +5,15 @@ At this point the only thing left to do is to create some components and use the
{format: "javascript"}
```
const SayNow = ({ dateTime }) => {
return ['h1', {}, [`It is: ${dateTime}`]];
return ['h1', {}, [`It is: ${dateTime}`]];
};
const App = () => {
return ['div', { 'className': 'header' },
[SayNow({ dateTime: new Date() }),
['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
]
];
return ['div', { 'className': 'header' },
[SayNow({ dateTime: new Date() }),
['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
]
];
}
render(createElement(App()), document.getElementById('root'));
@ -26,8 +26,8 @@ The next step is to call render multiple times.
{format: "javascript"}
```
setInterval(() =>
render(createElement(App()), document.getElementById('root')),
1000);
render(createElement(App()), document.getElementById('root')),
1000);
```
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 updated or replaced is the part that changes (aside from the DOM props). We now have a working version of our own React.

@ -26,13 +26,13 @@ While keeping that in mind, here is an overview of the algorithm we will be impl
{format: "javascript"}
```
if (!element && prevElement)
// delete dom element
// delete dom element
else if (element && !prevElement)
// add new dom element, render children
// add new dom element, render children
else if (element.type === prevElement.type)
// update dom element, render children
// update dom element, render children
else if (element.type !== prevElement.type)
// replace dom element, render children
// replace dom element, render children
```
Notice that in every case, except deletion, we still call `render` on the element's children. And while it's possible that the children will have their associated DOM elements reused, their `render` methods will still be invoked.
@ -45,10 +45,10 @@ Here we begin by adding a global object that will store our last render tree, ke
```
const renderTrees = {};
function render(element, container) {
const tree =
render_internal(element, container, renderTrees[container]);
// render complete, store the updated tree
renderTrees[container] = tree;
const tree =
render_internal(element, container, renderTrees[container]);
// render complete, store the updated tree
renderTrees[container] = tree;
}
```
@ -59,34 +59,34 @@ Now that we have stored our last render tree we can go ahead and update our rend
{format: "javascript"}
```
function render_internal(element, container, prevElement) {
let domElement, children;
if (!element && prevElement) {
removeDOMElement(prevElement);
return;
} else if (element && !prevElement) {
domElement = createDOMElement(element);
} else if (element.type === prevElement.type) {
domElement = prevElement.domElement;
} else { // types don't match
removeDOMElement(prevElement);
domElement = createDOMElement(element);
}
setDOMProps(element, domElement, prevElement);
children = renderChildren(element, domElement, prevElement);
if (!prevElement || domElement !== prevElement.domElement) {
container.appendChild(domElement);
}
return {
domElement: domElement,
parent: container,
type: element.type,
props: {
...element.props,
children: children
}
};
let domElement, children;
if (!element && prevElement) {
removeDOMElement(prevElement);
return;
} else if (element && !prevElement) {
domElement = createDOMElement(element);
} else if (element.type === prevElement.type) {
domElement = prevElement.domElement;
} else { // types don't match
removeDOMElement(prevElement);
domElement = createDOMElement(element);
}
setDOMProps(element, domElement, prevElement);
children = renderChildren(element, domElement, prevElement);
if (!prevElement || domElement !== prevElement.domElement) {
container.appendChild(domElement);
}
return {
domElement: domElement,
parent: container,
type: element.type,
props: {
...element.props,
children: children
}
};
}
```
@ -101,7 +101,7 @@ Removing a DOM element is straightforward; we just `removeChild` on the parent e
{format: "javascript"}
```
function removeDOMElement(prevElement) {
prevElement.parent.removeChild(prevElement.domElement);
prevElement.parent.removeChild(prevElement.domElement);
}
```
@ -110,9 +110,9 @@ In creating a new DOM element we just need to branch if we are creating a text e
{format: "javascript"}
```
function createDOMElement(element) {
return element.type === 'TEXT' ?
document.createTextNode(element.props.nodeValue) :
document.createElement(element.type);
return element.type === 'TEXT' ?
document.createTextNode(element.props.nodeValue) :
document.createElement(element.type);
}
```
@ -121,18 +121,18 @@ To set the props on an element, we first clear all the existing props and then l
{format: "javascript"}
```
function setDOMProps(element, domElement, prevElement) {
if (prevElement) {
Object.keys(prevElement.props)
.filter((key) => key !== 'children')
.forEach((key) => {
domElement[key] = ''; // clear prop
});
}
Object.keys(element.props)
.filter((key) => key !== 'children')
.forEach((key) => {
domElement[key] = element.props[key];
});
if (prevElement) {
Object.keys(prevElement.props)
.filter((key) => key !== 'children')
.forEach((key) => {
domElement[key] = ''; // clear prop
});
}
Object.keys(element.props)
.filter((key) => key !== 'children')
.forEach((key) => {
domElement[key] = element.props[key];
});
}
```
@ -145,17 +145,17 @@ For rendering children we use two loops. The first loop removes any elements tha
{format: "javascript"}
```
function renderChildren(element, domElement, prevElement = { props: { children: [] }}) {
const elementLen = element.props.children.length;
const prevElementLen = prevElement.props.children.length;
// remove now unused elements
for (let i = elementLen; i < prevElementLen - elementLen; i++) {
removeDOMElement(element.props.children[i]);
}
// render existing and new elements
return element.props.children.map((child, i) => {
const prevChild = i < prevElementLen ? prevElement.props.children[i] : undefined;
return render_internal(child, domElement, prevChild);
});
const elementLen = element.props.children.length;
const prevElementLen = prevElement.props.children.length;
// remove now unused elements
for (let i = elementLen; i < prevElementLen - elementLen; i++) {
removeDOMElement(element.props.children[i]);
}
// render existing and new elements
return element.props.children.map((child, i) => {
const prevChild = i < prevElementLen ? prevElement.props.children[i] : undefined;
return render_internal(child, domElement, prevChild);
});
}
```

@ -14,10 +14,10 @@ This is the same signature as that of React itself. We begin by just focusing on
{format: "javascript"}
```
function render(element, container) {
const domElement = createDOMElement(element);
setProps(element, domElement);
renderChildren(element, domElement);
container.appendChild(domElement);
const domElement = createDOMElement(element);
setProps(element, domElement);
renderChildren(element, domElement);
container.appendChild(domElement);
```
Our DOM element is created first. Then we set the properties, render children elements, and finally append the whole tree to the container.
@ -29,23 +29,24 @@ Now that we have an idea of what to build we will work on expanding the pseudoco
{format: "javascript"}
```
function render(element, container) {
const { type, props } = element;
const { type, props } = element;
// create the DOM element
const domElement = type === 'TEXT' ?
document.createTextNode(props.nodeValue) :
document.createElement(type);
// create the DOM element
const domElement = type === 'TEXT' ?
document.createTextNode(props.nodeValue) :
document.createElement(type);
// set its properties
Object.keys(props)
.filter((key) => key !== 'children')
.forEach((key) => domElement[key] = props[key]);
// set its properties
Object.keys(props)
.filter((key) => key !== 'children')
.forEach((key) => domElement[key] = props[key]);
// render its children
props.children.forEach((child) => render(child, domElement));
// render its children
props.children.forEach((child) =>
render(child, domElement));
// add our tree to the DOM!
container.appendChild(domElement);
// add our tree to the DOM!
container.appendChild(domElement);
}
```

Loading…
Cancel
Save