Created
August 11, 2023 10:57
-
-
Save KMahoney/1d12d6083c2db7ecaa44a342ac289688 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
// An example of integrating global state with React. | |
// Not necessarily recommended for production applications. | |
import { useSyncExternalStore } from "react"; | |
// Define the application's state as a global variable | |
type State = { counter: number }; | |
let state: State = { counter: 0 }; | |
// React needs to know when the state changes so it knows when | |
// to update components. Keep an array of callbacks to notify | |
// React of changes. | |
type Callback = () => void; | |
let subscribers: Callback[] = []; | |
function subscribe(callback: Callback) { | |
subscribers.push(callback); | |
} | |
function unsubscribe(callback: Callback) { | |
subscribers = subscribers.filter((cb) => cb !== callback); | |
} | |
// Mutate the state and notify subscribers. This should be the only way | |
// state is modified. | |
export function mutate(f: (state: State) => State) { | |
state = f(state); | |
for (const subscriber of subscribers) { | |
subscriber(); | |
} | |
} | |
// Use 'useSyncExternalStore' to integrate the application state with React. | |
// Optionally select a part of the state. | |
// It is important that the the selector function returns stable values i.e. | |
// it doesn't return a new array or object on every invocation. Otherwise, | |
// the React component will re-render on every frame. | |
export function useAppStateSelector<T>(f: (state: State) => T) { | |
return useSyncExternalStore( | |
(callback) => { | |
subscribe(callback); | |
return () => unsubscribe(callback); | |
}, | |
() => f(state) | |
); | |
} | |
// An example action | |
function incAction(state: State): State { | |
return { counter: state.counter + 1 }; | |
} | |
// An example component | |
function Component() { | |
const count = useAppStateSelector((state) => state.counter); | |
return ( | |
<div> | |
{count} | |
<button onClick={() => mutate(incAction)} /> | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment