Skip to content

Instantly share code, notes, and snippets.

@twfarland
Last active March 29, 2018 21:34
Show Gist options
  • Save twfarland/656aca7832a2c7796d3d0d844a36ba9f to your computer and use it in GitHub Desktop.
Save twfarland/656aca7832a2c7796d3d0d844a36ba9f to your computer and use it in GitHub Desktop.
Using Proxies as a less verbose redux replacement
// ---------- Store builder
export interface Initial<S> {
(): S
}
export interface Listener<S> {
(obj: S, prop?: (keyof S), value?: any): any
}
export interface Store<S> {
state: S
onChange(l: Listener<S>): void
}
export function makeStore<S extends object>(initial: () => S): Store<S> {
const listeners: Listener<S>[] = []
function onChange(l: Listener<S>): void {
listeners.push(l)
}
const handler = {
set(obj: S, prop: (keyof S), value) {
let cloned // set by value, break the reference
try {
cloned = JSON.parse(JSON.stringify(value))
} catch (err) {
throw new Error('Only json-serializable objects should be set')
}
obj[prop] = cloned
for (let listener of listeners) {
listener(obj, prop, cloned)
}
return true
}
}
const state: S = new Proxy<S>(initial(), handler)
return { state, onChange }
}
// --------- Usage
export interface State {
count: number
}
const STORAGE_KEY = '__KEY__'
export function initialState(): State {
const saved = localStorage.getItem(STORAGE_KEY)
return saved ? JSON.parse(saved) : {
count: 0
}
}
export const { state, onChange } = makeStore(initialState)
onChange(state => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
// Your router or top level view would be updated by this listener
console.log(state.count)
})
// trigger state changes by directly setting property on state proxy
// there's no need for boilerplate like dispatch, action creators, or reducers
state.count = 1
state.count = 2
state.count = 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment