Skip to content

Commit

Permalink
revision for lesson 3
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinb-khan committed Jul 4, 2024
1 parent e5a43dc commit 12b6808
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 13 deletions.
24 changes: 20 additions & 4 deletions src/react-render-perf/lesson-03/content.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,27 @@ Using our example from [Lesson 2](/react-render-perf/lesson-02) we can replace t
by creating an <code>EventEmitter</code> singleton and using it directly in our components.

```tsx
import {createContext, useContext, useState, useMemo} from "react";
import * as React from "react";
import EventEmitter from "eventemitter3";

const emitterSingleton = new EventEmitter();

const Foo = () => {
const [foo, setFoo] = useState<number>(0);
emitterSingleton.on("foo", setFoo);
React.useEffect(() => {
emitterSingleton.on("foo", setFoo);
return () => emitterSingleton.off("foo", setFoo);
});

return <h1>foo = {foo}</h1>;
};

const Bar = () => {
const [bar, setBar] = useState<number>(0);
emitterSingleton.on("bar", setBar);
React.useEffect(() => {
emitterSingleton.on("bar", setBar);
return () => emitterSingleton.off("bar", setBar);
});

return <h1>bar = {bar}</h1>;
};
Expand Down Expand Up @@ -66,14 +72,24 @@ const Parent = () => {

## Notes

### Testing
While singletons are sometimes frowned upon because they can make testing more
difficult in some languages, that isn't the case in JavaScript. It's easy to mock
the singleton using <code>jest</code> if follow these guidelines:

- Export the singleton from its own file.
- Export the singleton from its own file. TODO(kevinb): provide an example of how to
mock the singleton.
- If you have a custom class instead of using <code>EventEmitter</code> be sure to export
that as well so that you can use it when mocking the singleton.

### State

Using an event emitter means that we're responsible for updating the state. In the
example above, we've chosen to do that in the <code>Parent</code> component. We
could've also sub-classed <code>EventEmitter</code> and made it responsible for managing
state. Ideally though, I think we'll probably want to adopt a state management library
like <code>jotai</code>, <code>zustand</code>, or <code>recoil.js</code>.

## Exercise

The code in the exercise/ folder in this lesson is the same as the solution/ folder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ export class ColorPickerEventEmitter extends EventEmitter {
return () => this.off(color, callback);
}

public onSelectedColorChange(callback: (selectedColor: string) => void) {
public onSelectedColorChange(
callback: (selectedColor: string) => void,
): () => void {
this.on("any", callback);
return () => this.off("any", callback);
}

public selectColor(color: string) {
Expand Down
10 changes: 6 additions & 4 deletions src/react-render-perf/lesson-03/exercise/color-picker.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {useContext, useState} from "react";
import * as React from "react";

import Grid from "./grid";
import {ColorPickerContext} from "./color-picker-context";

export default function ColorPicker() {
const [color, setColor] = useState<string | undefined>(undefined);
const colorPickerEventEmitter = useContext(ColorPickerContext)!;
colorPickerEventEmitter.onSelectedColorChange(setColor);
const [color, setColor] = React.useState<string | undefined>(undefined);
const colorPickerEventEmitter = React.useContext(ColorPickerContext)!;
React.useEffect(() => {
return colorPickerEventEmitter.onSelectedColorChange(setColor);
}, []);

Check warning on line 11 in src/react-render-perf/lesson-03/exercise/color-picker.tsx

View workflow job for this annotation

GitHub Actions / Lint (ubuntu-latest, 20.x)

React Hook React.useEffect has a missing dependency: 'colorPickerEventEmitter'. Either include it or remove the dependency array

Check warning on line 11 in src/react-render-perf/lesson-03/exercise/color-picker.tsx

View workflow job for this annotation

GitHub Actions / Lint (ubuntu-latest, 20.x)

React Hook React.useEffect has a missing dependency: 'colorPickerEventEmitter'. Either include it or remove the dependency array

return (
<div>
Expand Down
4 changes: 4 additions & 0 deletions src/react-render-perf/lesson-03/exercise/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export default function Solution3() {
return (
<div>
<h1>Exercise 3: Avoid Using Context</h1>
<p>
In this exercise, we want you to remove the context and use the
event emitter directly.
</p>
<ColorPickerContext.Provider value={emitter}>
<ColorPicker />
</ColorPickerContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ class ColorPickerEventEmitter extends EventEmitter {
return () => this.off(color, callback);
}

public onSelectedColorChange(callback: (selectedColor: string) => void) {
public onSelectedColorChange(
callback: (selectedColor: string) => void,
): () => void {
this.on("any", callback);
return () => this.off("any", callback);
}

public selectColor(color: string) {
Expand Down
8 changes: 5 additions & 3 deletions src/react-render-perf/lesson-03/solution/color-picker.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {useState} from "react";
import * as React from "react";

import Grid from "./grid";
import {colorPickerEventEmitter} from "./color-picker-event-emitter";

export default function ColorPicker() {
const [color, setColor] = useState<string | undefined>(undefined);
colorPickerEventEmitter.onSelectedColorChange(setColor);
const [color, setColor] = React.useState<string | undefined>(undefined);
React.useEffect(() => {
return colorPickerEventEmitter.onSelectedColorChange(setColor);
}, []);

return (
<div>
Expand Down

0 comments on commit 12b6808

Please sign in to comment.