-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
FEI-5465.3: Create README.md for lesson 01
- Loading branch information
1 parent
6bada65
commit cecc902
Showing
13 changed files
with
225 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# React Render Perf | ||
|
||
The purpose of this workshop is to learn about common issues that can result | ||
react renders taking longer than expected. | ||
|
||
## Lessons | ||
|
||
1. Memoizing expensive to render components | ||
2. Prevent React.Context from re-render the whole tree | ||
3. Avoid using React.Context at all | ||
4. Minimizing re-renders by splitting up large components |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
# 01 - Memoizing Expensive Components | ||
|
||
Memoization can be used to avoid unnecessary renders. This is most useful when | ||
the component itself is expensive to render (e.g. the `MathJax` component in | ||
webapp) or it renders a lot of descedent components. | ||
|
||
Memoization works by saving the rendered output of the component based on the | ||
props that are being passed in. Often times props will appear to have changed | ||
when their actual values haven't. In JavaScript, two objects with the same | ||
properties are considered different objects. Similarly, two functions with the | ||
same implementation are considered differen objects. | ||
|
||
In order for memoization to have the desired benefit, we want don't want the | ||
component to rerender if there are only superficial changes to props. | ||
|
||
`React.memo(Component, arePropsEqual?)` by default does a shallow comparison of | ||
props and as such isn't able to determine when a prop that's on object or function | ||
is the same or not. We can pass a custom `arePropsEqual` function to override | ||
that behavior. To keep things simple we use a third-party library called | ||
`react-fast-compare` which provides a function that does a deep comparison of | ||
objects. | ||
|
||
```ts | ||
import arePropsEqual from "react-fast-compare"; | ||
|
||
type Props = { | ||
user: {name: string, id: string}, | ||
onClick: () => void, | ||
} | ||
|
||
const ChildComponent = (props: Props) => { | ||
// ... | ||
} | ||
|
||
export default React.memo(ChildComponent, arePropsEqual); | ||
``` | ||
|
||
There is a bit of a gotcha here when it comes to props that are functions. | ||
`react-fast-compare` cannot check if two functions are the same. Imagine the | ||
following scenario: | ||
|
||
```ts | ||
import ChildComponent from "./child-component"; | ||
|
||
type Props = { | ||
user: {name: string, id: string}, | ||
}; | ||
|
||
const ParentComponent = (props: Props) => { | ||
const result = useQuery(QUERY); | ||
|
||
const handleClick = () => { | ||
if (result.data) { | ||
// do something with the data | ||
} | ||
}; | ||
|
||
return <ChildComponent user={user} onClick={handleClick}> | ||
} | ||
``` | ||
|
||
Each time `ParentComponent` renders, a new copy of `handleClick` will be created | ||
even if the `result` from `useQuery` isn't ready yet. We only want this function | ||
to treated as a new function when `result` changes. React provides a hook called | ||
`useCallback` which does exactly that by memoizing the function. | ||
|
||
```ts | ||
import ChildComponent from "./my-component"; | ||
|
||
type Props = { | ||
user: {name: string, id: string}, | ||
}; | ||
|
||
const ParentComponent = (props: Props) => { | ||
const result = useQuery(QUERY); | ||
|
||
const handleClick = React.useCallback(() => { | ||
if (result.data) { | ||
// do something with the data | ||
} | ||
}, [result]); | ||
|
||
return <ChildComponent user={user} onClick={handleClick}> | ||
} | ||
``` | ||
|
||
If the `ParentComponent` is a class-based component, there is no need to memoize | ||
function props that are pre-bound methods. This is because the method never changes | ||
for the component instance. If the prop is an inline function though, it should be | ||
convered to a pre-bound method. | ||
|
||
```ts | ||
import ChildComponent from "./my-component"; | ||
|
||
type Props = { | ||
user: {name: string, id: string}, | ||
}; | ||
type State = { | ||
result?: Result<typeof QUERY>, | ||
} | ||
|
||
class ParentComponent extends React.Component<Props, State> { | ||
componentDidMount() { | ||
fetch(QUERY).then((result) => { | ||
this.setState({result}); | ||
}); | ||
} | ||
|
||
handleClick = () => { | ||
if (this.result?.data) { | ||
// do something with the data | ||
} | ||
} | ||
|
||
render() { | ||
return <ChildComponent user={user} onClick={handleClick}> | ||
} | ||
} | ||
``` | ||
|
||
**WARNING:** | ||
Memoization is not free. It requires memory so you should be picky when deciding | ||
what to memoize. | ||
|
||
| Good Candidates | Bad Candidates | | ||
| ------------------------------------------ | ---------------------------------------- | | ||
| lots of descendants | few descendants | | ||
| expensive to render | inexpensive to render | | ||
| actual values of props change infrequently | actual values of props change frequently | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function Exercise1() { | ||
return <h1>Exercise 1: Memoizing Expensive Components</h1>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# 02 - Prevent Context From Rerendering | ||
|
||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function Exercise2() { | ||
return <h1>Exercise 2: Prevent Context From Rendering</h1>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# 03 - Avoid Using Context | ||
|
||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function Exercise3() { | ||
return <h1>Exercise 3: Avoid Using Context</h1>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# 04 - Splitting Large Components | ||
|
||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function Exercise4() { | ||
return <h1>Exercise 4: Splitting up large components</h1>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import {BrowserRouter, Route} from "react-router-dom"; | ||
|
||
import TableOfContents from "./table-of-contents"; | ||
import Lesson1Exercise from "./lesson-01/exercise"; | ||
import Lesson2Exercise from "./lesson-02/exercise"; | ||
import Lesson3Exercise from "./lesson-03/exercise"; | ||
import Lesson4Exercise from "./lesson-04/exercise"; | ||
|
||
export default function Routes() { | ||
return ( | ||
<BrowserRouter> | ||
<Route path="/react-render-perf" exact={true}> | ||
<TableOfContents /> | ||
</Route> | ||
<Route path="/react-render-perf/01" exact={true}> | ||
<Lesson1Exercise /> | ||
</Route> | ||
<Route path="/react-render-perf/02" exact={true}> | ||
<Lesson2Exercise /> | ||
</Route> | ||
<Route path="/react-render-perf/03" exact={true}> | ||
<Lesson3Exercise /> | ||
</Route> | ||
<Route path="/react-render-perf/04" exact={true}> | ||
<Lesson4Exercise /> | ||
</Route> | ||
</BrowserRouter> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import {Link} from "react-router-dom"; | ||
|
||
export default function ReactRenderPerf() { | ||
return ( | ||
<> | ||
<Link to="/">Home</Link> | ||
<h1>React Render Perf</h1> | ||
<ul> | ||
<li> | ||
<Link to="/react-render-perf/01"> | ||
01 - Memoizing Expensive Components | ||
</Link> | ||
</li> | ||
<li> | ||
<Link to="/react-render-perf/02"> | ||
02 - Prevent Context From Rendering | ||
</Link> | ||
</li> | ||
<li> | ||
<Link to="/react-render-perf/03"> | ||
03 - Avoid Using Context | ||
</Link> | ||
</li> | ||
<li> | ||
<Link to="/react-render-perf/04"> | ||
04 - Splitting Up Large Components | ||
</Link> | ||
</li> | ||
</ul> | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters