Skip to content

Instantly share code, notes, and snippets.

@shqld
Created August 15, 2022 14:30
Show Gist options
  • Save shqld/2668d8bb8b8c31a2cc92237315fbce23 to your computer and use it in GitHub Desktop.
Save shqld/2668d8bb8b8c31a2cc92237315fbce23 to your computer and use it in GitHub Desktop.
Super simple state management system like Recoil
import { useState, useEffect } from "preact/hooks"
type Listener<T> = (state: T) => void
type Updater<T> = (state: T) => T
type Context<T> = {
setState(update: T | Updater<T>): void
subscribe(listener: Listener<T>): void
unsubscribe(listener: Listener<T>): void
getState(): T
}
export function useAtom<T>(context: Context<T>): [T, typeof context.setState] {
const [state, setState] = useState<T>(context.getState())
useEffect(() => {
context.subscribe(setState)
return () => context.unsubscribe(setState)
}, [])
return [state, context.setState]
}
const eventName = "state-change" as const
export function atom<T>(state: T): Context<T> {
const eventTarget = new EventTarget()
let listeners: Map<Listener<T>, EventListener> = new Map()
return {
getState(): T {
return state
},
setState(update: T | Updater<T>): void {
state =
typeof update === "function"
? (update as Updater<T>)(state)
: update
eventTarget.dispatchEvent(
new CustomEvent(eventName, { detail: state })
)
},
subscribe(listener: Listener<T>): void {
const raw = (event: Event) => {
listener((event as CustomEvent).detail)
}
listeners.set(listener, raw)
eventTarget.addEventListener(eventName, raw)
},
unsubscribe(listener: Listener<T>): void {
const raw = listeners.get(listener)
if (raw) {
eventTarget.removeEventListener(eventName, raw)
}
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment