Skip to content

Instantly share code, notes, and snippets.

@KMahoney
Created August 11, 2023 10:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save KMahoney/1d12d6083c2db7ecaa44a342ac289688 to your computer and use it in GitHub Desktop.
Save KMahoney/1d12d6083c2db7ecaa44a342ac289688 to your computer and use it in GitHub Desktop.
// 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