From 0a308ee27ec62c125412b9aba5b70823f2396637 Mon Sep 17 00:00:00 2001 From: Kevin Barabash Date: Thu, 4 Jul 2024 17:39:00 -0400 Subject: [PATCH] revision for lesson 3 --- src/react-render-perf/lesson-03/content.mdx | 24 +++++++++++++++---- .../exercise/color-picker-event-emitter.ts | 5 +++- .../lesson-03/exercise/color-picker.tsx | 10 ++++---- .../lesson-03/exercise/index.tsx | 4 ++++ .../solution/color-picker-event-emitter.ts | 5 +++- .../lesson-03/solution/color-picker.tsx | 8 ++++--- 6 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/react-render-perf/lesson-03/content.mdx b/src/react-render-perf/lesson-03/content.mdx index b018150..d88fd07 100644 --- a/src/react-render-perf/lesson-03/content.mdx +++ b/src/react-render-perf/lesson-03/content.mdx @@ -21,21 +21,27 @@ Using our example from [Lesson 2](/react-render-perf/lesson-02) we can replace t by creating an EventEmitter 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(0); - emitterSingleton.on("foo", setFoo); + React.useEffect(() => { + emitterSingleton.on("foo", setFoo); + return () => emitterSingleton.off("foo", setFoo); + }); return

foo = {foo}

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

bar = {bar}

; }; @@ -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 jest 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 EventEmitter 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 Parent component. We +could've also sub-classed EventEmitter and made it responsible for managing +state. Ideally though, I think we'll probably want to adopt a state management library +like jotai, zustand, or recoil.js. + ## Exercise The code in the exercise/ folder in this lesson is the same as the solution/ folder diff --git a/src/react-render-perf/lesson-03/exercise/color-picker-event-emitter.ts b/src/react-render-perf/lesson-03/exercise/color-picker-event-emitter.ts index 4b85bdc..3d3c45c 100644 --- a/src/react-render-perf/lesson-03/exercise/color-picker-event-emitter.ts +++ b/src/react-render-perf/lesson-03/exercise/color-picker-event-emitter.ts @@ -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) { diff --git a/src/react-render-perf/lesson-03/exercise/color-picker.tsx b/src/react-render-perf/lesson-03/exercise/color-picker.tsx index b498271..3f04768 100644 --- a/src/react-render-perf/lesson-03/exercise/color-picker.tsx +++ b/src/react-render-perf/lesson-03/exercise/color-picker.tsx @@ -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(undefined); - const colorPickerEventEmitter = useContext(ColorPickerContext)!; - colorPickerEventEmitter.onSelectedColorChange(setColor); + const [color, setColor] = React.useState(undefined); + const colorPickerEventEmitter = React.useContext(ColorPickerContext)!; + React.useEffect(() => { + return colorPickerEventEmitter.onSelectedColorChange(setColor); + }, []); return (
diff --git a/src/react-render-perf/lesson-03/exercise/index.tsx b/src/react-render-perf/lesson-03/exercise/index.tsx index 7144917..7d220d0 100644 --- a/src/react-render-perf/lesson-03/exercise/index.tsx +++ b/src/react-render-perf/lesson-03/exercise/index.tsx @@ -10,6 +10,10 @@ export default function Solution3() { return (

Exercise 3: Avoid Using Context

+

+ In this exercise, we want you to remove the context and use the + event emitter directly. +

diff --git a/src/react-render-perf/lesson-03/solution/color-picker-event-emitter.ts b/src/react-render-perf/lesson-03/solution/color-picker-event-emitter.ts index 159b6ae..adcef32 100644 --- a/src/react-render-perf/lesson-03/solution/color-picker-event-emitter.ts +++ b/src/react-render-perf/lesson-03/solution/color-picker-event-emitter.ts @@ -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) { diff --git a/src/react-render-perf/lesson-03/solution/color-picker.tsx b/src/react-render-perf/lesson-03/solution/color-picker.tsx index cbbdcca..0c66108 100644 --- a/src/react-render-perf/lesson-03/solution/color-picker.tsx +++ b/src/react-render-perf/lesson-03/solution/color-picker.tsx @@ -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(undefined); - colorPickerEventEmitter.onSelectedColorChange(setColor); + const [color, setColor] = React.useState(undefined); + React.useEffect(() => { + return colorPickerEventEmitter.onSelectedColorChange(setColor); + }, []); return (