Updates to chapter 1 and 2.

master
Thomas Hintz 4 years ago
parent 172f7b201f
commit bdc0b9cbb8

@ -1,3 +1,16 @@
var Hello = ({ dateTime }) => {
return ['h1', {}, [`It is: ${dateTime}`]];
};
var App = () => {
return ['div', { 'className': 'header' },
[Hello({ dateTime: new Date() }),
['input', { 'type': 'submit', 'disabled': 'disabled' }, []]
]
];
}
var d = var d =
['div', { 'className': 'header' }, ['div', { 'className': 'header' },
[['h1', {}, ['Hello']], [['h1', {}, ['Hello']],
@ -126,3 +139,10 @@ function renderChildren(element, domElement, prevElement = { props: { children:
return render_internal(child, domElement, prevChild); return render_internal(child, domElement, prevChild);
}); });
} }
var Hello = memo(({ dateTime }) => {
return ['h1', {}, [`It is: ${dateTime}`]];
});

@ -681,14 +681,65 @@ likely to occur from the underlying algorithms and not from the Fiber
specific details. In the chapter on Concurrent Mode we will go in to specific details. In the chapter on Concurrent Mode we will go in to
this more. this more.
** Putting it all together
Throughout the rest of the book we will be building on and using our
React implementation so it would be helpful to see it all put together
and working. 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' }, []]
]
];
}
render(createElement(App()), document.getElementById('root'));
#+END_SRC
We are just creating two components, based on the same JSX-like
notation we were using earlier. We create one ~prop~: ~dateTime~. It
gets passed to the ~SayNow~ component which just prints out the
DateTime passed in to it. To simplify our implementation we are just
passing props as object literals.
The next step is to just call render multiple times.
#+BEGIN_SRC javascript
setInterval(() =>
render(createElement(App()), document.getElementById('root')),
1000);
#+END_SRC
If you do that 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.
#+begin_note
This implementation is designed for teaching purposes and has some
known issues and bugs, like always updating the DOM props, along with
other things. Fundamentally, it functions the same as React but if you
wanted to use it in a more production setting it would take a lot more
development.
#+end_note
** Conclusion ** Conclusion
Of course our version of React elides over many details that React Of course our version of React elides over many details that React
must contend with like props, state, lifecycle methods, and hooks. For must contend with, like starting a re-render from where state changes
understanding how to build high-performance React applications, and event handlers. For understanding how to build high-performance
however, the most important piece to understand is how and when React React applications, however, the most important piece to understand is
renders components, which is what we have learned in creating our own how and when React renders components, which is what we have learned
mini version of React. in creating our own mini version of React.
At this point you should have an understanding of how React works. In At this point you should have an understanding of how React works. In
the rest of the book we are going to be refining this model and the rest of the book we are going to be refining this model and
@ -698,6 +749,8 @@ bottlenecks.
TODO maybe a graphic summarizing the heuristics? TODO maybe a graphic summarizing the heuristics?
TODO maybe show full example with our React
* Rendering Model * Rendering Model
:PROPERTIES: :PROPERTIES:
:EXPORT_FILE_NAME: manuscript/rendering-model.markua :EXPORT_FILE_NAME: manuscript/rendering-model.markua
@ -713,7 +766,7 @@ TODO insert img-tree of components
In figure 1, if state changes in component A but nothing changes in B In figure 1, if state changes in component A but nothing changes in B
will React ask B to re-render? will React ask B to re-render?
Yes. Absolutely. Always, unless ~componentShouldUpdate~ returns false, Yes. Absolutely. Always, unless ~shouldComponentUpdate~ returns false,
which is not even an option with functional components and is which is not even an option with functional components and is
discouraged for class based components. So if we have a large tree of discouraged for class based components. So if we have a large tree of
components and we change state high in the tree React will be components and we change state high in the tree React will be
@ -730,13 +783,13 @@ children. React is effectively off-loading the descision to re-render
to the components themselves because a general solution has poor to the components themselves because a general solution has poor
performance. performance.
Originally React had ~componentShouldUpdate~ to solve this issue but Originally React had ~shouldComponentUpdate~ to solve this issue but
the developers of React found that implementing it correctly was the developers of React found that implementing it correctly was
difficult and error prone. Programmers would add new props to a difficult and error prone. Programmers would add new props to a
component but forget to update ~componentShouldUpdate~ with the new component but forget to update ~shouldComponentUpdate~ with the new
props causing the component to not update when it should which led to props causing the component to not update when it should which led to
strange and hard to diagnose bugs. So if we shouldn't use strange and hard to diagnose bugs. So if we shouldn't use
~componentShouldUpdate~ what tools are we left with? ~shouldComponentUpdate~ what tools are we left with?
And it's a great question because unneeded renders can be a massive And it's a great question because unneeded renders can be a massive
bottleneck. Especially on large lists of components. In fact, there is bottleneck. Especially on large lists of components. In fact, there is
@ -769,6 +822,39 @@ React API itself, then we will extend our React implementation from
chapter one to support the same API. Then we will discuss its usage chapter one to support the same API. Then we will discuss its usage
and analyze when and how to use it. and analyze when and how to use it.
** ~React.memo~
The first API React provides that we will look at is
~React.memo~. ~React.memo~ is a higher-order component
(HOC) that wraps your functional component. It handles memoizing your
component based on its props (not state).
Here is the signature for ~React.memo~:
#+BEGIN_SRC javascript
function (Component, areEqual?) { ... }
#+END_SRC
It takes two arguments, one required and one optional. The required
argument is the component you want to memoize. The second and optional
argument is a function that allows you to tell React when your
component will produce the same output.
If the second argument is not specified then React performs a
/shallow/ comparison between props it has received in the past and the
current props. If the current props match props that have been passed
to your component before React will use the output stored from that
previous render instead of rendering your component again. If you want
more control over the prop comparison, like if you wanted to deeply
compare some props, you would pass in your own ~areEqual?~. However,
it's generally recommended to program in a more pure style instead of
using ~areEqual?~ because it can suffer from the same problem that
~shouldComponentUpdate~ did.
** ~React.PureComponent~
TODO useCallback TODO useCallback
* Diagnosing Bottlenecks * Diagnosing Bottlenecks

@ -366,12 +366,50 @@ The actual React implementation used to look very similar to what we've gone thr
But even with such a large change, the underlying algorithms for deciding how and when to render components is the same. And when not running in Concurrent Mode the effect is still the same as React does the render phase in one block still. So using a simplified interpretation that doesn't include all the complexities of breaking up the process in to chunks enables us to see more clearly how the process as a whole works. At this point bottlenecks are much more likely to occur from the underlying algorithms and not from the Fiber specific details. In the chapter on Concurrent Mode we will go in to this more. But even with such a large change, the underlying algorithms for deciding how and when to render components is the same. And when not running in Concurrent Mode the effect is still the same as React does the render phase in one block still. So using a simplified interpretation that doesn't include all the complexities of breaking up the process in to chunks enables us to see more clearly how the process as a whole works. At this point bottlenecks are much more likely to occur from the underlying algorithms and not from the Fiber specific details. In the chapter on Concurrent Mode we will go in to this more.
## Putting it all together
Throughout the rest of the book we will be building on and using our React implementation so it would be helpful to see it all put together and working. At this point the only thing left to do is to create some components and use them!
{format: "javascript"}
```
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'));
```
We are just creating two components, based on the same JSX-like notation we were using earlier. We create one `prop`: `dateTime`. It gets passed to the `SayNow` component which just prints out the DateTime passed in to it. To simplify our implementation we are just passing props as object literals.
The next step is to just call render multiple times.
{format: "javascript"}
```
setInterval(() =>
render(createElement(App()), document.getElementById('root')),
1000);
```
If you do that 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.
I> This implementation is designed for teaching purposes and has some known issues and bugs, like always updating the DOM props, along with other things. Fundamentally, it functions the same as React but if you wanted to use it in a more production setting it would take a lot more development.
## Conclusion ## Conclusion
Of course our version of React elides over many details that React must contend with like props, state, lifecycle methods, and hooks. For understanding how to build high-performance React applications, however, the most important piece to understand is how and when React renders components, which is what we have learned in creating our own mini version of React. Of course our version of React elides over many details that React must contend with, like starting a re-render from where state changes and event handlers. For understanding how to build high-performance React applications, however, the most important piece to understand is how and when React renders components, which is what we have learned in creating our own mini version of React.
At this point you should have an understanding of how React works. In the rest of the book we are going to be refining this model and looking at practical applications of it so that we are prepared to build high performance React applications and diagnose any bottlenecks. At this point you should have an understanding of how React works. In the rest of the book we are going to be refining this model and looking at practical applications of it so that we are prepared to build high performance React applications and diagnose any bottlenecks.
TODO maybe a graphic summarizing the heuristics? TODO maybe a graphic summarizing the heuristics?
TODO maybe show full example with our React

@ -6,11 +6,11 @@ TODO insert img-tree of components
In figure 1, if state changes in component A but nothing changes in B will React ask B to re-render? In figure 1, if state changes in component A but nothing changes in B will React ask B to re-render?
Yes. Absolutely. Always, unless `componentShouldUpdate` returns false, which is not even an option with functional components and is discouraged for class based components. So if we have a large tree of components and we change state high in the tree React will be constantly re-rendering large parts of the tree. (This is common because app state often has to live up high in the tree because props can only be passed down.) This is clearly very in efficient so why does React do it? Yes. Absolutely. Always, unless `shouldComponentUpdate` returns false, which is not even an option with functional components and is discouraged for class based components. So if we have a large tree of components and we change state high in the tree React will be constantly re-rendering large parts of the tree. (This is common because app state often has to live up high in the tree because props can only be passed down.) This is clearly very in efficient so why does React do it?
If you remember back to when we implemented the render algorithm you'll recall that React does nothing to see if a component actually needs to re-render, it only cares tests whether DOM elements need to be replaced or removed. Instead React always renders all children. React is effectively off-loading the descision to re-render to the components themselves because a general solution has poor performance. If you remember back to when we implemented the render algorithm you'll recall that React does nothing to see if a component actually needs to re-render, it only cares tests whether DOM elements need to be replaced or removed. Instead React always renders all children. React is effectively off-loading the descision to re-render to the components themselves because a general solution has poor performance.
Originally React had `componentShouldUpdate` to solve this issue but the developers of React found that implementing it correctly was difficult and error prone. Programmers would add new props to a component but forget to update `componentShouldUpdate` with the new props causing the component to not update when it should which led to strange and hard to diagnose bugs. So if we shouldn't use `componentShouldUpdate` what tools are we left with? Originally React had `shouldComponentUpdate` to solve this issue but the developers of React found that implementing it correctly was difficult and error prone. Programmers would add new props to a component but forget to update `shouldComponentUpdate` with the new props causing the component to not update when it should which led to strange and hard to diagnose bugs. So if we shouldn't use `shouldComponentUpdate` what tools are we left with?
And it's a great question because unneeded renders can be a massive bottleneck. Especially on large lists of components. In fact, there is no other way to control renders; React will always render. And it's a great question because unneeded renders can be a massive bottleneck. Especially on large lists of components. In fact, there is no other way to control renders; React will always render.
@ -28,6 +28,23 @@ This is indeed such a common bottleneck and solution that React provides an API
We will learn about this API by first looking at the signatures of the React API itself, then we will extend our React implementation from chapter one to support the same API. Then we will discuss its usage and analyze when and how to use it. We will learn about this API by first looking at the signatures of the React API itself, then we will extend our React implementation from chapter one to support the same API. Then we will discuss its usage and analyze when and how to use it.
## `React.memo`
The first API React provides that we will look at is `React.memo`. `React.memo` is a higher-order component (HOC) that wraps your functional component. It handles memoizing your component based on its props (not state).
Here is the signature for `React.memo`:
{format: "javascript"}
```
function (Component, areEqual?) { ... }
```
It takes two arguments, one required and one optional. The required argument is the component you want to memoize. The second and optional argument is a function that allows you to tell React when your component will produce the same output.
If the second argument is not specified then React performs a *shallow* comparison between props it has received in the past and the current props. If the current props match props that have been passed to your component before React will use the output stored from that previous render instead of rendering your component again. If you want more control over the prop comparison, like if you wanted to deeply compare some props, you would pass in your own `areEqual?`. However, it's generally recommended to program in a more pure style instead of using `areEqual?` because it can suffer from the same problem that `shouldComponentUpdate` did.
## `React.PureComponent`
TODO useCallback TODO useCallback

Loading…
Cancel
Save