UNPKG

nanostores

Version:

A tiny (265 bytes) state manager for React/Preact/Vue/Svelte with many atomic tree-shakable stores

161 lines (146 loc) 4.17 kB
import { clean } from '../clean-stores/index.js' const START = 0 const STOP = 1 const SET = 2 const NOTIFY = 3 const MOUNT = 5 const UNMOUNT = 6 const REVERT_MUTATION = 10 export let on = (object, listener, eventKey, mutateStore) => { object.events = object.events || {} if (!object.events[eventKey + REVERT_MUTATION]) { object.events[eventKey + REVERT_MUTATION] = mutateStore(eventProps => { // eslint-disable-next-line no-sequences object.events[eventKey].reduceRight((event, l) => (l(event), event), { shared: {}, ...eventProps }) }) } object.events[eventKey] = object.events[eventKey] || [] object.events[eventKey].push(listener) return () => { let currentListeners = object.events[eventKey] let index = currentListeners.indexOf(listener) currentListeners.splice(index, 1) if (!currentListeners.length) { delete object.events[eventKey] object.events[eventKey + REVERT_MUTATION]() delete object.events[eventKey + REVERT_MUTATION] } } } export let onStart = ($store, listener) => on($store, listener, START, runListeners => { let originListen = $store.listen $store.listen = arg => { if (!$store.lc && !$store.starting) { $store.starting = true runListeners() delete $store.starting } return originListen(arg) } return () => { $store.listen = originListen } }) export let onStop = ($store, listener) => on($store, listener, STOP, runListeners => { let originOff = $store.off $store.off = () => { runListeners() originOff() } return () => { $store.off = originOff } }) export let onSet = ($store, listener) => on($store, listener, SET, runListeners => { let originSet = $store.set let originSetKey = $store.setKey if ($store.setKey) { $store.setKey = (changed, changedValue) => { let isAborted let abort = () => { isAborted = true } runListeners({ abort, changed, newValue: { ...$store.value, [changed]: changedValue } }) if (!isAborted) return originSetKey(changed, changedValue) } } $store.set = newValue => { let isAborted let abort = () => { isAborted = true } runListeners({ abort, newValue }) if (!isAborted) return originSet(newValue) } return () => { $store.set = originSet $store.setKey = originSetKey } }) export let onNotify = ($store, listener) => on($store, listener, NOTIFY, runListeners => { let originNotify = $store.notify $store.notify = (oldValue, changed) => { let isAborted let abort = () => { isAborted = true } runListeners({ abort, changed, oldValue }) if (!isAborted) return originNotify(oldValue, changed) } return () => { $store.notify = originNotify } }) export let STORE_UNMOUNT_DELAY = 1000 export let onMount = ($store, initialize) => { let listener = payload => { let destroy = initialize(payload) if (destroy) $store.events[UNMOUNT].push(destroy) } return on($store, listener, MOUNT, runListeners => { let originListen = $store.listen $store.listen = (...args) => { if (!$store.lc && !$store.active) { $store.active = true runListeners() } return originListen(...args) } let originOff = $store.off $store.events[UNMOUNT] = [] $store.off = () => { originOff() setTimeout(() => { if ($store.active && !$store.lc) { $store.active = false for (let destroy of $store.events[UNMOUNT]) destroy() $store.events[UNMOUNT] = [] } }, STORE_UNMOUNT_DELAY) } if (process.env.NODE_ENV !== 'production') { let originClean = $store[clean] $store[clean] = () => { for (let destroy of $store.events[UNMOUNT]) destroy() $store.events[UNMOUNT] = [] $store.active = false originClean() } } return () => { $store.listen = originListen $store.off = originOff } }) }