UNPKG

mobx-miniprogram-bindings

Version:
153 lines (136 loc) 4.23 kB
import { reaction, comparer, toJS } from 'mobx-miniprogram' import { IStoreBindings } from './index' export const createActions = <TStore extends Record<string, any>>(methods, options: IStoreBindings<TStore>) => { const { store, actions } = options if (!actions) return // for array-typed fields definition if (typeof store === 'undefined') { throw new Error('[mobx-miniprogram] no store specified') } if (Array.isArray(actions)) { actions.forEach((field) => { if (methods[field]) { throw new Error('[mobx-miniprogram] multiple action definition') } methods[field] = (...args) => { return (store[field] as (...args: unknown[]) => unknown)(...args) } }) } else if (typeof actions === 'object') { Object.keys(actions).forEach((field) => { const def = actions[field] if (typeof field !== 'string' && typeof field !== 'number') { throw new Error('[mobx-miniprogram] unrecognized field definition') } methods[field] = (...args) => { return (store[def] as (...args: unknown[]) => unknown)(...args) } }) } } export type StoreBindingsManager = { updateStoreBindings: () => void destroyStoreBindings: () => void } export const createDataFieldsReactions = <TStore extends Record<string, any>>( target, options: Omit<IStoreBindings<TStore>, 'actions'>, ): StoreBindingsManager => { const { store, fields, structuralComparison } = options // if use namespace let namespace = options.namespace || '' if (namespace && typeof namespace !== 'string') { throw new Error('[mobx-miniprogram] namespace only expect string') } namespace = namespace.replace(/ /gm, '') let namespaceData = Object.assign({}, target[namespace]) const useNamespace = (): boolean => { return namespace !== '' } // choose equal method const equals = structuralComparison ? comparer.structural : undefined // setData combination let pendingSetData: Record<string, any> | null = null const applySetData = () => { if (pendingSetData === null) return const data = pendingSetData pendingSetData = null target.setData(data) } const scheduleSetData = (field, value) => { if (!pendingSetData) { pendingSetData = {} if (typeof wx !== 'undefined') wx.nextTick(applySetData) else Promise.resolve().then(applySetData) } if (useNamespace()) { namespaceData = { ...namespaceData, [field]: toJS(value), } pendingSetData[namespace] = namespaceData } else { pendingSetData[field] = toJS(value) } } // handling fields let reactions: (() => void)[] = [] if (Array.isArray(fields)) { // for array-typed fields definition if (typeof store === 'undefined') { throw new Error('[mobx-miniprogram] no store specified') } reactions = fields.map((field) => { return reaction( () => store[field], (value) => { scheduleSetData(field, value) }, { equals, fireImmediately: true, }, ) }) } else if (typeof fields === 'object' && fields) { // for object-typed fields definition reactions = Object.keys(fields).map((field) => { const def = fields[field] if (typeof def === 'function') { return reaction( () => def.call(target, store), (value) => { scheduleSetData(field, value) }, { equals, fireImmediately: true, }, ) } if (typeof field !== 'string' && typeof field !== 'number') { throw new Error('[mobx-miniprogram] unrecognized field definition') } if (typeof store === 'undefined') { throw new Error('[mobx-miniprogram] no store specified') } return reaction( () => store[def], (value) => { scheduleSetData(String(field), value) }, { equals, fireImmediately: true, }, ) }) } const destroyStoreBindings = () => { reactions.forEach((reaction) => reaction()) } return { updateStoreBindings: applySetData, destroyStoreBindings, } }