UNPKG

unstated-enhancers

Version:
172 lines (135 loc) 4.27 kB
import * as unstated from 'unstated' import PersistConfig from './support/PersistConfig' import queue from './support/queue'; const __SUPER_SECRET_CONTAINER_DEBUG_HOOK__ = (unstated as any).__SUPER_SECRET_CONTAINER_DEBUG_HOOK__ let PERSIST_ENABLED: boolean = false; class Persist { private __containers: any private key: string private prefix: string private version: number private debounce: number private storage: any private state: any; private persistStatePartial: any constructor() { this.__containers = {}; this.state = {}; this.debounce = 250; this.prefix = 'unstated::' } config(config: PersistConfig) { for (const [key, value] of Object.entries(config)) { this[key] = value; } this.persistStatePartial = { _persist_version: this.version, __action: 'REHYDRATE' } } async clear() { this.state = {}; for (const [key, container] of Object.entries(this.__containers)) { (container as any).state = { ...(container as any).__containerInitialState, _persist_version: (container as any).state._persist_version }; this.state[key] = (container as any).state; } await this.flush(); } async flush() { await this.storage.removeItem(this.key); } containers() { return this.__containers; } start(config?: PersistConfig) { if (config) { this.config(config); } PERSIST_ENABLED = true; this.key = this.prefix + this.key __SUPER_SECRET_CONTAINER_DEBUG_HOOK__((container: any) => { const bootstrap = () => this.__bootstrap(container) queue(bootstrap); }) } async __bootstrap(container: any) { if (container.persist) { const config = container.persist this.__containers[config.key] = container await this.rehydrate(container) } } async rehydrate(container: any) { const config = container.persist const key = config.key const version = config.version || this.version const containerDefaultState: any = container.state || {} container.hydrated = false; container.__containerInitialState = containerDefaultState; try { let serialState = await this.storage.getItem(this.key); if (serialState !== null) { this.state = JSON.parse(serialState) let newState = this.state[key]; // NOTE: // No migrations yet, just clear state. // Can be added later with similar api to redux-persist. if (!newState || newState._persist_version !== version) { if (process.env.NODE_ENV !== 'production') { const prevVersion = newState ? newState._persist_version : -1; console.log(`unstated-persist: state version mismatch for ${key} (${prevVersion} vs ${version}), skipping rehydration`) } await container.setState({ ...this.persistStatePartial, ...containerDefaultState }) } else { // State versions match, set state as is await container.setState({ ...newState, __action: 'REHYDRATE' }) } } else { await container.setState({ ...this.persistStatePartial, ...containerDefaultState }) } } catch (err) { await container.setState({ ...this.persistStatePartial, ...containerDefaultState }) if (process.env.NODE_ENV !== 'production') { console.log("unstated-persist: Error during rehydate", err); } } finally { let debounceId: any; container.hydrated = true; // Dont start PERSIST_ENABLED until rehydration is complete container.subscribe(() => { if (debounceId) { clearTimeout(debounceId); } debounceId = setTimeout(() => { this.state[key] = container.state; this.storage .setItem(this.key, JSON.stringify(this.state)) .catch((err) => { if (process.env.NODE_ENV !== 'production') { console.log("unstated-persist: Error on persist state", err) } }) }, this.debounce) }); } }; } export { PERSIST_ENABLED } export default Persist