Formatting updates.
This commit is contained in:
@@ -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);
|
||||
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);
|
||||
}
|
||||
if (!prevElement || domElement !== prevElement.domElement) {
|
||||
container.appendChild(domElement);
|
||||
}
|
||||
|
||||
return {
|
||||
domElement: domElement,
|
||||
parent: container,
|
||||
type: element.type,
|
||||
props: {
|
||||
...element.props,
|
||||
children: children
|
||||
}
|
||||
};
|
||||
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 SayNow = ({ dateTime }) => {
|
||||
return ['h1', {}, [`It is: ${dateTime}`]];
|
||||
};
|
||||
|
||||
const App = () => {
|
||||
return ['div', { 'className': 'header' },
|
||||
[SayNow({ dateTime: new Date() }),
|
||||
['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
|
||||
]
|
||||
];
|
||||
}
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user