Skip to content

Latest commit

 

History

History
85 lines (66 loc) · 2.88 KB

File metadata and controls

85 lines (66 loc) · 2.88 KB

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.

While singletons are often frowned upon, their use in this scenario offers several benefits over React Context:

  • By default Context will often result in unnecessary re-renders while using a singleton will not.
  • 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 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 we can replace the context by creating a EventEmitter singleton and using it directly in our components.

import {createContext, useContext, useState, useMemo} from "react";
import EventEmitter from "eventemitter3";

const emitterSingleton = new EventEmitter();

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

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

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

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

const Parent = () => {
    const [foo, setFoo] = useState<number>(0);
    const [bar, setBar] = useState<number>(0);

    const incrementFoo = () => {
        emitterSingleton.emit("foo", foo + 1);
        setFoo(foo + 1);
    };
    const incrementBar = () => {
        emitterSingleton.emit("bar", bar + 1);
        setBar(bar + 1);
    }

    <>
        <FooBarContext.Provider value={value}>
            <Foo/>
            <Bar/>
        </FooBarContext.Provider>
        <button onClick={incrementFoo}>increment foo</button>
        <button onClick={incrementBar}>increment bar</button>
    </>
};

Notes

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.
  • 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.

Exercise

The code in the exercise/ folder in this lesson is the same as the solution/ folder for the Lesson 2.

  1. Use the profiler in React dev tools to measure the render performance of the code in the "exercise" folder.
  2. Replace the React Context in the code with a singleton.
  3. Use the profiler in React dev tools to measure the render performance again.