UNPKG

refer

Version:

redux-like library for handling global state on functional style

134 lines (112 loc) 3.27 kB
import { isThenable, isFn, isObj, isArr } from './types' import combineHandlers from './combineHandlers' import createDispatch from './createDispatch' import mapValues from './mapValues' import * as LIFE_CYCLE from './constants' const { GET_TABLE, SHOULD_DISPATCH, DISPATCH, WILL_UPDATE, SHOULD_UPDATE, DID_UPDATE, THROW_ERROR, ASYNC_START, ASYNC_END, SYNC } = LIFE_CYCLE let createStore = (innerDispatch, initialState = {}) => { if (isArr(innerDispatch)) { innerDispatch = createDispatch(combineHandlers(...innerDispatch)) } else if (isObj(innerDispatch)) { innerDispatch = createDispatch(innerDispatch) } if (!isFn(innerDispatch)) { throw new Error(`Expected the innerDispatch to be a function which is ${ innerDispatch }`) } let listeners = [] let subscribe = listener => { listeners.push(listener) return () => { let index = listeners.indexOf(listener) if (index !== -1) { listeners.splice(index, 1) } } } let currentState = initialState let replaceState = (nextState, silent, ...args) => { currentState = nextState if (!silent) { listeners.forEach(listener => listener(...args)) } } let updateCurrentState = data => { if (innerDispatch(SHOULD_UPDATE, data) !== false) { innerDispatch(WILL_UPDATE, data) replaceState(data.nextState, null, data) innerDispatch(DID_UPDATE, data) } } let getState = () => currentState let dispatchError = error => Promise.reject(innerDispatch(THROW_ERROR, error)) let isDispatching = false let dispatch = (key, value) => { if (isDispatching) { throw new Error(`store.dispatch(key, value): handler may not dispatch`) } let currentData = { currentState, key, value } if (innerDispatch(SHOULD_DISPATCH, currentData) === false) { return currentState } innerDispatch(DISPATCH, currentData) let nextState let isAsync = false let getNextState = f => f(currentState) let handlerNextState = nextState => { let data if (isAsync) { data = { currentState, nextState, key, value } updateCurrentState(data) innerDispatch(ASYNC_END, data) } return nextState } try { isDispatching = true nextState = innerDispatch([key, getNextState, handlerNextState], value) } catch(error) { return dispatchError(error) } finally { isDispatching = false } if (nextState === currentState) { return currentState } let data = { currentState, nextState, key, value } if (!isThenable(nextState)) { updateCurrentState(data) innerDispatch(SYNC, data) return currentState } innerDispatch(ASYNC_START, data) isAsync = true return nextState.catch(error => { innerDispatch(ASYNC_END, { currentState, key, value, error }) return dispatchError(error) }) } let createActions = obj => mapValues(obj, (_, key) => value => LIFE_CYCLE.hasOwnProperty(key) ? innerDispatch(key, value) : dispatch(key, value) ) let actions = createActions(innerDispatch(GET_TABLE)) return { dispatch, actions, getState, replaceState, subscribe, createActions } } export default createStore