Skip to content

Commit

Permalink
Minor edits for performance lessons (#8)
Browse files Browse the repository at this point in the history
I've addressed the minor edits I noticed and removed them from the [Google doc](https://docs.google.com/document/d/1q4SON_aZOudU89h5zDBtDciIc9DzbTP4G884_G2MvZw/edit?usp=sharing)! Let me know if you have any questions

Author: beaesguerra

Reviewers: kevinb-khan, kevinbarabash

Required Reviewers:

Approved By: kevinb-khan

Checks: ✅ Lint (ubuntu-latest, 20.x), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ✅ Lint (ubuntu-latest, 20.x), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x)

Pull Request URL: #8
  • Loading branch information
beaesguerra authored Jul 3, 2024
1 parent 46091a2 commit 42c90f9
Show file tree
Hide file tree
Showing 7 changed files with 19 additions and 90 deletions.
16 changes: 8 additions & 8 deletions src/react-render-perf/lesson-01/content.mdx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
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
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
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
In order for memoization to have the desired benefit, we 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
props and as such isn't able to determine when a prop that's an 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
Expand Down Expand Up @@ -53,7 +53,7 @@ const ParentComponent = (props: Props) => {
}
};

return <ChildComponent user={user} onClick={handleClick}>
return <ChildComponent user={props.user} onClick={handleClick}>
}
```

Expand Down Expand Up @@ -84,8 +84,8 @@ const ParentComponent = (props: Props) => {

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, e.g.
`onClick={() => { ... }}` it should be convered to a pre-bound method, see the
for the component instance. If the prop is an inline function though, e.g.
`onClick={() => { ... }}` it should be converted to a pre-bound method, see the
example below.
```ts
Expand All @@ -112,7 +112,7 @@ class ParentComponent extends React.Component<Props, State> {
}
}

render() {
render() {
return <ChildComponent user={user} onClick={handleClick}>
}
}
Expand Down
13 changes: 7 additions & 6 deletions src/react-render-perf/lesson-02/content.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ descendents to be re-rendered. If the number of descendents is large, this can
result in poor rendering performance.

In order to avoid re-rendering all of the descendents we'd like only those
components using the context to re-render. Ideally, the should only re-render
components using the context to re-render. Ideally, they should only re-render
if the data that they're making use of changes.

In the following example, everytime we update the `value` in the context, we
re-render both child components of the context even though only one of them
needs to be update.
needs to be updated.

```tsx filename="index.tsx"
import {createContext, useContext, useState} from "react";
Expand All @@ -34,7 +34,7 @@ const Bar = () => {
const Parent = () => {
const [value, setValue] = useState<FooBar>({foo: 0, bar: 0});
const incrementFoo = () => setValue(fb => {...fb, foo: fb.foo + 1});
const incrementBar = () => setValue(fb => {...fb, foo: fb.foo + 1});
const incrementBar = () => setValue(fb => {...fb, bar: fb.bar + 1});
<>
<FooBarContext.Provider value={value}>
<Foo/>
Expand Down Expand Up @@ -92,7 +92,7 @@ const Parent = () => {
}

<>
<FooBarContext.Provider value={value}>
<FooBarContext.Provider value={emitter}>
<Foo/>
<Bar/>
</FooBarContext.Provider>
Expand All @@ -110,22 +110,23 @@ props so this is trivial to do.

```tsx
import {createContext, useContext, useState, memo} from "react";
import arePropsEqual from "react-fast-compare";

const Foo = memo(() => {
const emitter = useContext(FooBarContext);
const [foo, setFoo] = useState<number>(0);
emitter?.on("foo", setFoo);

return <h1>foo = {foo}</h1>;
});
}, arePropsEqual);

const Bar = memo(() => {
const emitter = useContext(FooBarContext);
const [bar, setBar] = useState<number>(0);
emitter?.on("bar", setBar);

return <h1>bar = {bar}</h1>;
});
}, arePropsEqual);
```

## Exercise
Expand Down
24 changes: 0 additions & 24 deletions src/react-render-perf/lesson-02/exercise/child.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/react-render-perf/lesson-02/exercise/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {ColorPicker} from "./color-picker";
export default function Exercise2() {
return (
<div>
<h1>Solution 2: Prevent Context From Rendering</h1>
<h1>Exercise 2: Prevent Context From Rendering</h1>
<ColorPicker />
</div>
);
Expand Down
24 changes: 0 additions & 24 deletions src/react-render-perf/lesson-02/solution/child.tsx

This file was deleted.

6 changes: 3 additions & 3 deletions src/react-render-perf/lesson-03/content.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
If your context contains an event emitter (or other class instance) and it's only
If your context contains an event emitter (or other class instance) and it's only
used once in your application, you can simplify things even further by using a
singleton instance of it.

Expand All @@ -10,15 +10,15 @@ benefits over React Context:
- Context is only available in React so if other code needs access to that data
we need to figure out some way to pipe that data to that code whereas with a
singleton, that code can just import it.
- Testing components that use React Context require wrapper the component in an
- Testing components that use React Context require wrapping the component in an
appropriate provider which isn't that bad on its own but often times you'll need
to update the value in the Context. Updates are easier with a singleton. The
only thing we need is a way to reset the singleton before each test.

## Example

Using our example from [Lesson 2](/react-render-perf/lesson-02) we can replace the context
by creating a `EventEmitter` singleton and using it directly in our components.
by creating an `EventEmitter` singleton and using it directly in our components.

```tsx
import {createContext, useContext, useState, useMemo} from "react";
Expand Down
24 changes: 0 additions & 24 deletions src/react-render-perf/lesson-03/exercise/child.tsx

This file was deleted.

0 comments on commit 42c90f9

Please sign in to comment.