Adding more articles pages.
parent
963b4c2703
commit
3f04166894
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@
|
||||
"use client"
|
||||
|
||||
import { Article } from '@/components/Components';
|
||||
import PageContents from './page.mdx';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Article
|
||||
title="Learn JavaScript Fundamentals: Arrays"
|
||||
datetime="2021-08-31"
|
||||
datetitle="August 31st, 2021"
|
||||
author="Mackenzie Hanna"
|
||||
>
|
||||
<PageContents />
|
||||
</Article>
|
||||
);
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
"use client"
|
||||
|
||||
import { Article } from '@/components/Components';
|
||||
import PageContents from './page.mdx';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Article
|
||||
title="Next.js vs Create-React-App"
|
||||
datetime="2021-08-12"
|
||||
datetitle="August 12th, 2021"
|
||||
author="Mackenzie Hanna"
|
||||
>
|
||||
<PageContents />
|
||||
</Article>
|
||||
);
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
"use client"
|
||||
|
||||
import { Article } from '@/components/Components';
|
||||
import PageContents from './page.mdx';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Article
|
||||
title="An Overview of React Hooks"
|
||||
datetime="2021-08-20"
|
||||
datetitle="August 20th, 2021"
|
||||
author="Mackenzie Hanna"
|
||||
>
|
||||
<PageContents />
|
||||
</Article>
|
||||
);
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
"use client"
|
||||
|
||||
import { Article } from '@/components/Components';
|
||||
import PageContents from './page.mdx';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Article
|
||||
title="When should you use React.memo and useMemo?"
|
||||
datetime="2020-07-17"
|
||||
datetitle="July 17th, 2020"
|
||||
author="Thomas Hintz"
|
||||
>
|
||||
<PageContents />
|
||||
</Article>
|
||||
);
|
||||
}
|
@ -0,0 +1,282 @@
|
||||
To improve performance of your functional components you can use
|
||||
[`React.memo`](https://reactjs.org/docs/react-api.html#reactmemo) and
|
||||
[`useMemo`](https://reactjs.org/docs/hooks-reference.html#usememo).
|
||||
`React.memo` is a higher-order component (HOC) that wraps your
|
||||
functional component. It then memoizes its rendered output. `useMemo` is
|
||||
a hook that gets run during the render stage and also memoizes its
|
||||
output based on an array of dependency inputs. Both of these methods
|
||||
require you to program in a "pure" style. This means given the same prop
|
||||
values you always render the same output. `useState` and `useContext`
|
||||
hooks will also trigger a re-render for components that use
|
||||
`React.memo`.
|
||||
|
||||
If you're working with function based components you should read on but
|
||||
if you are working with class based components you can learn how to
|
||||
achieve the same thing with `React.PureComponent` in my article "[When
|
||||
should you use
|
||||
React.PureComponent?](./when-should-you-use-react-purecomponent)".
|
||||
|
||||
React.memo
|
||||
------------
|
||||
|
||||
``` {.javascript}
|
||||
const Component = React.memo(MyComponent, areEqual);
|
||||
```
|
||||
|
||||
`React.memo` takes one required argument, your React component, and one
|
||||
optional argument, a comparison function.
|
||||
|
||||
### Eligibility
|
||||
|
||||
``` {.javascript}
|
||||
// Example 1
|
||||
// Example of a "pure" component
|
||||
// Returns an H1 denoting whether the pancake was over-cooked
|
||||
|
||||
const CookedPancake = ({ batter, bakingTime, maxTime }) => {
|
||||
if (bakingTime > maxTime) {
|
||||
return (<h1>{batter} Over-cooked!</h1>);
|
||||
}
|
||||
return (<h1>{batter} Cooked Just Perfect!</h1>);
|
||||
};
|
||||
```
|
||||
|
||||
In the first example we just define a pure component and you can see
|
||||
that if the props are the same the rendered output will be the same and
|
||||
the component doesn't reference outside state or use random numbers.
|
||||
This is the first step in determining if a component is eligible for
|
||||
`React.memo`.
|
||||
|
||||
The next step is determining if the same props are passed in to the
|
||||
component multiple times or not. Like let's say we are using the
|
||||
`CookedPancake` component in an app that runs a simulation of baking
|
||||
times. The app starts simulating baking times from 0 to 10 incrementing
|
||||
the baking time by 1 for each iteration. In this case `bakingTime` is
|
||||
not a good candidate for `React.memo` because the `bakingTime` prop is
|
||||
different every time React goes to render the component so memoization
|
||||
will provide no benefit.
|
||||
|
||||
On the other hand let's say we have multiple different types of pancake
|
||||
batters in a list and the user is able to re-arrange the order in which
|
||||
they cook the pancakes. The prop values will be reused every time React
|
||||
re-renders the list of `CookedPancake` components. And since React may
|
||||
re-render each `CookedPancake` whenever the order of the list changes
|
||||
this means we have the right conditions for `React.memo`.
|
||||
|
||||
### Usage
|
||||
|
||||
``` {.javascript}
|
||||
// Example 2
|
||||
// Memoized component
|
||||
|
||||
const CookedPancake = React.memo(({ batter, bakingTime, maxTime }) => {
|
||||
if (bakingTime > maxTime) {
|
||||
return (<h1>{batter} Over-cooked!</h1>);
|
||||
}
|
||||
return (<h1>{batter} Cooked Just Perfect!</h1>);
|
||||
});
|
||||
|
||||
// Render CookedPancake with the same prop values.
|
||||
const MyPancakes = () => {
|
||||
return (
|
||||
<>
|
||||
<CookedPancake batter='Swedish'
|
||||
bakingTime=1
|
||||
maxTime=1.5
|
||||
/>
|
||||
<CookedPancake batter='Swedish'
|
||||
bakingTime=1
|
||||
maxTime=1.5
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
In Example 2 we add `React.memo` to the component definition and add the
|
||||
`MyPancakes` component that renders two Swedish `CookedPancake`. When
|
||||
React renders the first `CookedPancake` it will run the component's
|
||||
render code like normal but when it goes to render the second
|
||||
`CookedPancake` it will find a stored result matching the same prop
|
||||
values and instead of running the `CookedPancake` render code it will
|
||||
just use the result of its stored run (from the first `CookedPancake`
|
||||
render). It should also be noted that if `CookedPancake` had any child
|
||||
components their render code would also not be run since their output is
|
||||
a part of the memoized result. This means you can stop React from
|
||||
rendering an entire portion of your component tree with `React.memo`.
|
||||
|
||||
### Pitfalls
|
||||
|
||||
``` {.javascript}
|
||||
// Example 3
|
||||
// WRONG, this will not behave as expected
|
||||
|
||||
// A button that takes an onClick handler and some text.
|
||||
const Button = React.memo(({ onClick, text }) => {
|
||||
return (<button onClick={onClick} text={text});
|
||||
};
|
||||
|
||||
// Use the Button component and log to console when clicked.
|
||||
const LogButton = () => {
|
||||
return (
|
||||
<Button onClick={() => console.log('Log button clicked!')}
|
||||
text='Log'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<>
|
||||
<LogButton/>
|
||||
<LogButton/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
It's important to remember that `React.memo` use shallow comparison of
|
||||
props so passing anonymous functions or Javascript literals will
|
||||
probably not do what you want, as we can see in Example 3. In this
|
||||
example we create a simple button that allows an `onClick` handler to be
|
||||
set and some text for the button. Then we create a `LogButton` that just
|
||||
logs to the console when it is clicked (and this is where the bug is
|
||||
too). The `App` component just includes two `LogButton` components. If
|
||||
this behaved the way we might expect then we would expect React to not
|
||||
re-render `LogButton` when it gets to the second `LogButton` in `App`
|
||||
because it appears as if the props to `Button` are the same. But, in
|
||||
fact, they are not the same at all. The `onClick` handler is an
|
||||
anonymous function that Javascript creates every time it renders that
|
||||
code. So even though what the function does is constant a new Javascript
|
||||
object is allocated every time. This means that the memoization function
|
||||
always thinks the props have changed and will always re-render the
|
||||
component. Not what we want.
|
||||
|
||||
``` {.javascript}
|
||||
// Example 4
|
||||
// Re-write LogButton to work with Button's React.memo
|
||||
|
||||
const logButtonClicked = () => console.log('Log button clicked!');
|
||||
|
||||
const LogButton = () => {
|
||||
return (
|
||||
<Button onClick={logButtonClicked}
|
||||
text='Log'
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
In Example 4 we can see that moving the definition of the logging
|
||||
function outside the render path fixes the issue because now the same
|
||||
function object is always passed to `Button`.
|
||||
|
||||
``` {.javascript}
|
||||
// Example 5
|
||||
// WRONG, does not work as expected
|
||||
|
||||
// We change Button to take text as an object
|
||||
const Button = React.memo(({ onClick, attributes }) => {
|
||||
return (<button onClick={onClick} text={attributes.text});
|
||||
};
|
||||
|
||||
const LogButton = () => {
|
||||
return (
|
||||
<Button onClick={logButtonClicked}
|
||||
attributes={{ text: 'Log' }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
Let's say we wanted our button to take an object of attributes that we
|
||||
could use instead of adding a new prop for every attribute of our
|
||||
button. This might seem straightforward at first, as we can see in
|
||||
Example 5 with `{ text: 'Log' }`. But this suffers the same flaw as
|
||||
Example 3 with the anonymous function. Every time React renders
|
||||
`LogButton` it will create a new object for `attributes` breaking the
|
||||
memoization. In this case the fix is the same as for the anonymous
|
||||
function: don't create object literals in the render path.
|
||||
|
||||
### `areEqual`
|
||||
|
||||
What if you want to use `React.memo` but the provided shallow comparison
|
||||
is not enough for your use case? Maybe there is an inexpensive way you
|
||||
can do a deeper comparison? Well React thought of that too and allows
|
||||
you to specify a second argument to `React.memo`: `areEqual`.
|
||||
|
||||
``` {.javascript}
|
||||
// areEqual signature
|
||||
function areEqual(prevProps, nextProps) {}
|
||||
|
||||
// Usage with React.memo
|
||||
React.memo((props) => ..., areEqual);
|
||||
```
|
||||
|
||||
`areEqual` is a function you provide to React that receives `prevProps`
|
||||
and `nextProps`. You return `true` if passing both `prevProps` and
|
||||
`nextProps` to your component would yield the same result. Note that
|
||||
this is the opposite of how `shouldComponentUpdate` works where
|
||||
returning `true` would cause a re-render. Inside `areEqual` is where you
|
||||
would do a deep(er) comparison.
|
||||
|
||||
### Children
|
||||
|
||||
Also make sure that all child components of your memoized component
|
||||
follow the same rules otherwise they won't re-render when you expect
|
||||
them to.
|
||||
|
||||
`useMemo`
|
||||
---------
|
||||
|
||||
``` {.javascript}
|
||||
const memoizedValue =
|
||||
useMemo(() => expensiveComputation(x, y), [x, y]);
|
||||
```
|
||||
|
||||
`useMemo` is just a memoization function implemented as a hook that
|
||||
React provides. It takes two arguments. The first is a function that
|
||||
performs an expensive computation and the second are the variable
|
||||
dependencies of that function.
|
||||
|
||||
``` {.javascript}
|
||||
// Example 6
|
||||
// A pancake component visually representing the pancake's height.
|
||||
|
||||
const YummyPancake = ({ batterAmount, batterViscosity }) => {
|
||||
const thickness = useMemo(
|
||||
() => calculateThickness(batterAmount, batterViscosity),
|
||||
[batterAmount, batterViscosity]);
|
||||
|
||||
return (
|
||||
<div className='pancake' style={{ height: thickness }} />
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
Here we define a `YummyPancake` component that renders a div with its
|
||||
height determined by the calculated thickness of the pancake. Since
|
||||
calculating the thickness of a pancake is a very expensive operation and
|
||||
we might be making many pancakes with the same batter amount and batter
|
||||
viscosity (very precise chefs) we take advantage of `useMemo`.
|
||||
|
||||
In the example `calculateThickness` is called only if `batterAmount` and
|
||||
`batterViscosity`, the dependent variables, do not have the same values
|
||||
of any other time that `calculateThickness` was called. This means that
|
||||
all dependencies of your expensive function should be in the dependency
|
||||
array. If they are not then React may not call your function to
|
||||
calculate a new value when you are expecting it to. You'll also notice
|
||||
that `calculateThickness` is inside an anonymous function. This delays
|
||||
evaluation so that `useMemo` can decide if and when to execute
|
||||
`calculateThickness`.
|
||||
|
||||
In many ways `useMemo` follows the same rules and guidelines as
|
||||
`React.memo`. `useMemo` only does a shallow comparison on its
|
||||
dependencies and expects your expensive function to be pure. So be
|
||||
careful not to use object literals or anonymous functions in your
|
||||
dependencies.
|
||||
|
||||
In summary, you should use `React.memo` and `useMemo` when rendering is
|
||||
a bottleneck and you are working with pure functions, props, and
|
||||
arguments. And if you want to learn more about high performance React
|
||||
programming subscribe to the email list or the [atom feed](./feed.atom).
|
@ -0,0 +1,17 @@
|
||||
"use client"
|
||||
|
||||
import { Article } from '@/components/Components';
|
||||
import PageContents from './page.mdx';
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Article
|
||||
title="When should you use React.PureComponent?"
|
||||
datetime="2020-07-19"
|
||||
datetitle="July 19th, 2020"
|
||||
author="Thomas Hintz"
|
||||
>
|
||||
<PageContents />
|
||||
</Article>
|
||||
);
|
||||
}
|
@ -0,0 +1,245 @@
|
||||
To improve performance of your class based components you can use
|
||||
[`React.PureComponent`](https://reactjs.org/docs/react-api.html#reactpurecomponent).
|
||||
`React.PureComponent` is like `React.Component` except it implements
|
||||
`componentShouldUpdate` for you. The implementation it provides memoizes
|
||||
the component based on its props and state using a shallow comparison.
|
||||
To be able to utilize `React.PureComponent` it is required that you
|
||||
program in a "pure" style. This means given the same prop and state
|
||||
values you always render the same output.
|
||||
|
||||
If you're working with class based components you should read on but if
|
||||
you are working with functional components you can learn how to achieve
|
||||
the same thing with `React.memo` and `useMemo` in my article "[When
|
||||
should you use React.memo and
|
||||
useMemo?](./when-should-you-use-react-memo-and-usememo)".
|
||||
|
||||
`React.PureComponent`
|
||||
---------------------
|
||||
|
||||
``` {.javascript}
|
||||
class Pancake extends React.PureComponent {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
`React.PureComponent` just replaces the usual `extends React.Component`
|
||||
with `extends React.PureComponent`.
|
||||
|
||||
### Eligibility
|
||||
|
||||
Like for `React.memo` and `useMemo` we must first make sure that our
|
||||
component is eligible for `React.PureComponent`.
|
||||
|
||||
``` {.javascript}
|
||||
// Example 1
|
||||
// Example of a "pure" component
|
||||
// Returns an H1 denoting whether the pancake was over-cooked
|
||||
|
||||
class CookedPancake extends React.Component {
|
||||
render() {
|
||||
const { batter, bakingTime, maxTime } = this.props;
|
||||
if (bakingTime > maxTime) {
|
||||
return (<h1>{batter} Over-cooked!</h1>);
|
||||
}
|
||||
return (<h1>{batter} Cooked Just Perfect!</h1>);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the first example we just define a pure component and you can see
|
||||
that if the props are the same the rendered output will be the same and
|
||||
the component doesn't reference outside state or use random numbers.
|
||||
This is the first step in determining if a component is eligible for
|
||||
`React.PureComponent`.
|
||||
|
||||
The next step is determining if the same props are passed in to the
|
||||
component multiple times or not. Like let's say we are using the
|
||||
`CookedPancake` component in an app that runs a simulation of baking
|
||||
times. The app starts simulating baking times from 0 to 10 incrementing
|
||||
the baking time by 1 for each iteration. In this case `bakingTime` is
|
||||
not a good candidate for `React.memo` because the `bakingTime` prop is
|
||||
different every time React goes to render the component so memoization
|
||||
will provide no benefit.
|
||||
|
||||
On the other hand let's say we have multiple different types of pancake
|
||||
batters in a list and the user is able to re-arrange the order in which
|
||||
they cook the pancakes. The prop values will be reused every time React
|
||||
re-renders the list of `CookedPancake` components. And since React may
|
||||
re-render each `CookedPancake` whenever the order of the list changes
|
||||
this means we have the right conditions for `React.PureComponent`.
|
||||
|
||||
### Usage
|
||||
|
||||
``` {.javascript}
|
||||
// Example 2
|
||||
// Memoized component
|
||||
|
||||
// Only the class signature has changed:
|
||||
class CookedPancake extends React.PureComponent {
|
||||
render() {
|
||||
const { batter, bakingTime, maxTime } = this.props;
|
||||
if (bakingTime > maxTime) {
|
||||
return (<h1>{batter} Over-cooked!</h1>);
|
||||
}
|
||||
return (<h1>{batter} Cooked Just Perfect!</h1>);
|
||||
}
|
||||
}
|
||||
|
||||
// Render CookedPancake with the same prop values.
|
||||
class MyPancakes extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<CookedPancake batter='Swedish'
|
||||
bakingTime=1
|
||||
maxTime=1.5
|
||||
/>
|
||||
<CookedPancake batter='Swedish'
|
||||
bakingTime=1
|
||||
maxTime=1.5
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
In Example 2 we add `React.PureComponent` to the component class
|
||||
definition and add the `MyPancakes` component that renders two Swedish
|
||||
`CookedPancake`. When React renders the first `CookedPancake` it will
|
||||
run the component's render code like normal but when it goes to render
|
||||
the second `CookedPancake` it will find a stored result matching the
|
||||
same prop values and instead of running the `CookedPancake` render code
|
||||
it will just use the result of its stored run (from the first
|
||||
`CookedPancake` render). It should also be noted that if `CookedPancake`
|
||||
had any child components their render code would also not be run since
|
||||
their output is a part of the memoized result. This means you can stop
|
||||
React from rendering an entire portion of your component tree with
|
||||
`React.PureComponent`.
|
||||
|
||||
### Pitfalls
|
||||
|
||||
``` {.javascript}
|
||||
// Example 3
|
||||
// WRONG, this will not behave as expected
|
||||
|
||||
// A button that takes an onClick handler and some text.
|
||||
class Button extends React.PureComponent {
|
||||
render() {
|
||||
const { onClick, text } = this.props;
|
||||
return (<button onClick={onClick} text={text});
|
||||
}
|
||||
};
|
||||
|
||||
// Use the Button component and log to console when clicked.
|
||||
class LogButton extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Button onClick={() => console.log('Log button clicked!')}
|
||||
text='Log'
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
<LogButton/>
|
||||
<LogButton/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
It's important to remember that `React.PureComponent` use shallow
|
||||
comparison of props so passing anonymous functions or Javascript
|
||||
literals will probably not do what you want, as we can see in Example 3.
|
||||
In this example we create a simple button that allows an `onClick`
|
||||
handler to be set and some text for the button. Then we create a
|
||||
`LogButton` that just logs to the console when it is clicked (and this
|
||||
is where the bug is too). The `App` component just includes two
|
||||
`LogButton` components. If this behaved the way we might expect then we
|
||||
would expect React to not re-render `LogButton` when it gets to the
|
||||
second `LogButton` in `App` because it appears as if the props to
|
||||
`Button` are the same. But, in fact, they are not the same at all. The
|
||||
`onClick` handler is an anonymous function that Javascript creates every
|
||||
time it renders that code. So even though what the function does is
|
||||
constant a new Javascript object is allocated every time. This means
|
||||
that the memoization function always thinks the props have changed and
|
||||
will always re-render the component. Not what we want.
|
||||
|
||||
``` {.javascript}
|
||||
// Example 4
|
||||
// Re-write LogButton to work with Button's PureComponent usage
|
||||
|
||||
const logButtonClicked = () => console.log('Log button clicked!');
|
||||
|
||||
class LogButton extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Button onClick={logButtonClicked}
|
||||
text='Log'
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
In Example 4 we can see that moving the definition of the logging
|
||||
function outside the render path fixes the issue because now the same
|
||||
function object is always passed to `Button`.
|
||||
|
||||
``` {.javascript}
|
||||
// Example 5
|
||||
// WRONG, does not work as expected
|
||||
|
||||
// We change Button to take text as an object
|
||||
class Button extends React.PureComponent {
|
||||
render() {
|
||||
const { onClick, attributes } = this.props;
|
||||
return (<button onClick={onClick} text={attributes.text});
|
||||
}
|
||||
};
|
||||
|
||||
class LogButton extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Button onClick={logButtonClicked}
|
||||
attributes={{ text: 'Log' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Let's say we wanted our button to take an object of attributes that we
|
||||
could use instead of adding a new prop for every attribute of our
|
||||
button. This might seem straightforward at first, as we can see in
|
||||
Example 5 with `{ text: 'Log' }`. But this suffers the same flaw as
|
||||
Example 3 with the anonymous function. Every time React renders
|
||||
`LogButton` it will create a new object for `attributes` breaking the
|
||||
memoization. In this case the fix is the same as for the anonymous
|
||||
function: don't create object literals in the render path.
|
||||
|
||||
So far we've just been looking at props but React also includes the
|
||||
component's state in the memoization so make sure you follow the same
|
||||
rules for state as you would for props.
|
||||
|
||||
If you have nested state and still want to use `React.PureComponent` you
|
||||
can do so by utilizing React's `forceUpdate` API but this not
|
||||
recommended as it can lead to code that is more difficult to understand
|
||||
and use.
|
||||
|
||||
### Children
|
||||
|
||||
Also make sure that all child components of your memoized component
|
||||
follow the same rules otherwise they won't re-render when you expect
|
||||
them to.
|
||||
|
||||
In summary, you should use `React.PureComponent` when rendering is a
|
||||
bottleneck and you are working with pure components, props, state, and
|
||||
arguments. And if you want to learn more about high performance React
|
||||
programming subscribe to the email list or the [atom feed](./feed.atom).
|
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
@ -0,0 +1,5 @@
|
||||
import { H2, P } from '@/components/Components';
|
||||
|
||||
export function useMDXComponents(components) {
|
||||
return { h2: H2, p: P, ...components };
|
||||
}
|
Loading…
Reference in New Issue