Last active
December 17, 2020 17:49
-
-
Save 101arrowz/0964129f4b24f224b278defc2a654c65 to your computer and use it in GitHub Desktop.
Shared React state
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
import { useEffect, useState } from 'react'; | |
type Subscribable<T> = { | |
trigger(newValue: T): void; | |
subscribe(listener: (value: T) => unknown): number; | |
unsubscribe(ind: number): void; | |
}; | |
const createSubscribable = <T = void>(): Subscribable<T> => { | |
const subs: ((value: T) => unknown)[] = []; | |
const trigger = (value: T) => { | |
for (const sub of subs) { | |
sub(value); | |
} | |
}; | |
const subscribe = (listener: (value: T) => unknown) => { | |
return subs.push(listener) - 1; | |
} | |
const unsubscribe = (ind: number) => { | |
subs.splice(ind, 1); | |
} | |
return { | |
trigger, | |
subscribe, | |
unsubscribe | |
}; | |
} | |
const createHook = <T = void>(initialState: T, key?: string) => { | |
const subscribable = createSubscribable<T>(); | |
if (key) { | |
const lsKey = 'react-store-' + key; | |
const rehydrated = localStorage.getItem(lsKey); | |
if (rehydrated) initialState = JSON.parse(rehydrated); | |
subscribable.subscribe(newState => { | |
// beforeunload event is reliable but can fail during a crash, so persist | |
// on update. | |
localStorage.setItem(lsKey, JSON.stringify(newState)); | |
}); | |
} | |
const useSubscription = () => { | |
const [state, setState] = useState<T>(initialState); | |
useEffect(() => { | |
const id = subscribable.subscribe(setState); | |
return () => subscribable.unsubscribe(id); | |
}, [setState]); | |
return [state, (newState: T) => { | |
subscribable.trigger(newState); | |
}]; | |
} | |
return useSubscription; | |
} | |
// For example: | |
// const useSidebarOpen = createHook(false); | |
// const useFirstLogin = createHook(false, 'firstLogin'); | |
// These can be used as if it were useState but with no parameters | |
// Whenever one component calls setState from the hook, all subscribers receive | |
// the update | |
// useFirstLogin will automatically persist to and rehydrate from localStorage |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment