From 61a526dfeb1f1a8d8dfedc2f3ea5a04bb527490c Mon Sep 17 00:00:00 2001 From: Michael Fatemi Date: Sun, 11 Apr 2021 00:06:55 -0400 Subject: [PATCH] add store --- src/components/PoolMap.tsx | 2 + src/store.ts | 88 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 src/store.ts diff --git a/src/components/PoolMap.tsx b/src/components/PoolMap.tsx index 73e9123..db1a236 100644 --- a/src/components/PoolMap.tsx +++ b/src/components/PoolMap.tsx @@ -17,6 +17,8 @@ export default function PoolMap() { console.log('Loaded Base Map')} + onTilesLoaded={() => console.log('Loaded Tile Map')} > diff --git a/src/store.ts b/src/store.ts new file mode 100644 index 0000000..9479715 --- /dev/null +++ b/src/store.ts @@ -0,0 +1,88 @@ +import { useEffect, useState } from 'react'; + +export type UpdateListener = (newValue: StoreValue | null) => void; + +export type ValueFetcher = (key: string) => Promise; + +export type StoreValue = { value: T } | { error: any }; + +/** + * This is a general-purpose, subscribable key-value store for content like posts, groups, and spaces. + */ +export class Store { + /** + * Stores the internal data. If the value is `null`, then we attempted to fetch the data, but it did not exist. + */ + private data = new Map | null>(); + private listeners = new Map>>(); + + constructor(private fetcher: ValueFetcher) {} + + /** + * + * @param key The key to get the data for + * @param forceRefresh If the data already exists, fetch it again anyway + */ + get(key: string, forceRefresh = false): StoreValue | null { + if (!this.data.has(key) || forceRefresh) { + this.fetcher(key) + .then((value) => { + this.set(key, value ? { value } : null); + }) + .catch((error) => { + this.set(key, { error }); + }); + + return null; + } + + return this.data.get(key) ?? null; + } + + set(key: string, value: StoreValue | null) { + if (this.listeners.has(key)) { + this.listeners.get(key)?.forEach((callback) => { + callback(value); + }); + } + + return this.data.set(key, value); + } + + subscribe(key: string, listener: UpdateListener) { + if (!this.listeners.has(key)) { + this.listeners.set(key, new Set()); + } + + this.listeners.get(key)?.add(listener); + } + + unsubscribe(key: string, listener: UpdateListener) { + if (this.listeners.has(key)) { + if (this.listeners.get(key)?.has(listener)) { + this.listeners.get(key)?.delete(listener); + return; + } + } + + console.warn( + 'Unsubscribed from', + key, + 'but listener does not exist: ', + listener + ); + } +} + +export function useStoredValue(store: Store, key: string) { + const [value, setValue] = useState | null>(store.get(key)); + + useEffect(() => { + const callback = (value: StoreValue | null) => setValue(value); + store.subscribe(key, callback); + + return () => store.unsubscribe(key, callback); + }, [key, store]); + + return value; +}