Created
May 13, 2020 22:45
-
-
Save twfarland/36230c0e5b007a69ff0203617d4c7754 to your computer and use it in GitHub Desktop.
createStore hooks to share state across components, a lightweight and simpler replacement for redux or even context
This file contains 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 { useState, useEffect, Dispatch, SetStateAction } from "react" | |
type SetState<S> = Dispatch<SetStateAction<S>> | |
/* | |
Shares state amongst all instances, without using context API | |
This is a more lightweight redux alternative | |
Note that the initial state is set outside of any mounted instance, | |
as in redux. | |
*/ | |
export type UseStore<S> = readonly [S, (nextState: S) => void] | |
export interface Persister<S> { | |
write(state: S): void | |
read(): S | |
} | |
export function memoryPersister<S>(initialState: S): Persister<S> { | |
let state: S = initialState | |
const write = (nextState: S) => { | |
state = nextState | |
} | |
const read = () => state | |
return { write, read } | |
} | |
export function localPersister<S>(initialState: S, key: string): Persister<S> { | |
let state: S | |
const stored = localStorage.getItem(key) | |
state = stored ? (JSON.parse(stored) as S) : initialState | |
const write = (nextState: S) => { | |
if (nextState === null || nextState === undefined) { | |
localStorage.removeItem(key) | |
} else { | |
localStorage.setItem(key, JSON.stringify(nextState)) | |
} | |
state = nextState | |
} | |
const read = () => state | |
return { write, read } | |
} | |
export function createStore<S>(persister: Persister<S>) { | |
let listeners: SetState<S>[] = [] | |
function setState(nextState: S) { | |
persister.write(nextState) | |
// notify others of change | |
listeners.forEach((setStateInstance) => { | |
setStateInstance(nextState) | |
}) | |
} | |
return function useStore(): UseStore<S> { | |
const state = persister.read() | |
const setStateInstance = useState<S>(state)[1] | |
useEffect(() => { | |
listeners.push(setStateInstance) // listen for changes on other instances | |
return () => { | |
// remove listener on unmount | |
listeners = listeners.filter( | |
(listener) => listener !== setStateInstance | |
) | |
} | |
}, [setStateInstance]) // only run once | |
// use the shared state/setState, not the instance | |
return [state, setState] as const | |
} | |
} | |
export function createMemoryStore<S>(initialState: S) { | |
return createStore(memoryPersister(initialState)) | |
} | |
export function createLocalStore<S>(initialState: S, key: string) { | |
return createStore(localPersister(initialState, key)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example usage: