UNPKG

@turbox3d/reactivity

Version:

Large-scale reactive state management library

301 lines 10.6 kB
import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray"; import { nextTick, includes, isPromise, batchRemoveFromSet } from '@turbox3d/shared'; import { triggerCollector } from './collector'; import { EMaterialType } from '../const/enums'; import { Reaction } from './reactive'; import { ctx } from '../const/config'; import { EEventName, emitter } from '../utils/event'; import { materialCallStack } from '../utils/materialCallStack'; import { TimeTravel } from './time-travel'; export var store; export var actionTypeChain = []; var batchedUpdates = []; export var registerExternalBatchUpdate = function registerExternalBatchUpdate(obj) { batchedUpdates.push(obj); }; export function createStore(enhancer) { if (enhancer !== void 0) { store = enhancer(createStore); return store; } var componentUUIDToListeners = new WeakMap(); function subscribe(listener, uuid, idCustomType) { var isSubscribed = true; var obj = componentUUIDToListeners.get(uuid); if (obj === void 0 || obj.listeners === void 0) { componentUUIDToListeners.set(uuid, { idCustomType: idCustomType, listeners: [listener] }); } else if (!includes(obj.listeners, listener)) { componentUUIDToListeners.set(uuid, { idCustomType: idCustomType, listeners: obj.listeners.concat(listener) }); } return function unsubscribe() { if (!isSubscribed) { return; } isSubscribed = false; if (componentUUIDToListeners.has(uuid)) { componentUUIDToListeners["delete"](uuid); } }; } var dirtyJob; var mutationDepth = 0; var called = false; var flushChange = function flushChange(ids, useExternalBatchUpdate, isInner) { if (!ids.length || ctx.disableReactive) { return; } var pendingObjs = []; for (var index = 0; index < ids.length; index++) { var cid = ids[index]; var obj = componentUUIDToListeners.get(cid); if (obj) { pendingObjs.push(obj); } } var flush = function flush(idCustomType) { return function () { for (var _index = 0; _index < pendingObjs.length; _index++) { var _obj = pendingObjs[_index]; if (_obj.idCustomType === idCustomType) { var listeners = _obj.listeners; for (var j = 0; j < listeners.length; j++) { var listener = listeners[j]; listener(isInner); } } } }; }; if (useExternalBatchUpdate) { batchedUpdates.forEach(function (obj) { var batchUpdate = obj.handler; batchUpdate(flush(obj.idCustomType), function () { // finish batchUpdate ctx.batchUpdateOnFinish && ctx.batchUpdateOnFinish(); }); }); } else { // reverse flush()(); } }; var keepAliveComputed = function keepAliveComputed(isInner) { var computedReactionIds = _toConsumableArray(triggerCollector.waitTriggerIds.values()).filter(function (id) { return id instanceof Reaction && !id.lazy && id.computed; }); if (!computedReactionIds.length) { return; } if (ctx.devTool) { console.log('keepAlive computed', computedReactionIds); } flushChange(computedReactionIds, false, isInner); // clean wait queue keep alive batchRemoveFromSet(triggerCollector.waitTriggerIds, computedReactionIds); }; var immediatelyReactive = function immediatelyReactive(isInner) { var immediatelyReactionIds = _toConsumableArray(triggerCollector.waitTriggerIds.values()).filter(function (id) { return id instanceof Reaction && !id.computed && id.immediately; }); if (!immediatelyReactionIds.length) { return; } // clean wait queue batchRemoveFromSet(triggerCollector.waitTriggerIds, immediatelyReactionIds); if (ctx.devTool) { console.log('immediately reactive', immediatelyReactionIds); } flushChange(immediatelyReactionIds, false, isInner); }; var nextTickQueue = []; var nextTickCaller = function nextTickCaller() { if (called) { return; } called = true; var func = function func() { nextTick(function () { if (mutationDepth > 0) { called = false; nextTickQueue.shift(); return; } if (nextTickQueue.length === 0) { return; } dirtyJob && dirtyJob(); }); }; nextTickQueue.push(func); func(); }; var clean = function clean() { var needClearHistory = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; called = false; dirtyJob = void 0; nextTickQueue.shift(); actionTypeChain.length = 0; needClearHistory && triggerCollector.endBatch(); }; function dispatch(dispatchedAction) { var _a, _b, _c, _d; var payload = dispatchedAction.payload, type = dispatchedAction.type, original = dispatchedAction.original, immediately = dispatchedAction.immediately, _dispatchedAction$isI = dispatchedAction.isInner, isInner = _dispatchedAction$isI === void 0 ? false : _dispatchedAction$isI, domain = dispatchedAction.domain, name = dispatchedAction.name, _dispatchedAction$for = dispatchedAction.forceSaveHistory, forceSaveHistory = _dispatchedAction$for === void 0 ? false : _dispatchedAction$for, _dispatchedAction$isN = dispatchedAction.isNeedRecord, isNeedRecord = _dispatchedAction$isN === void 0 ? true : _dispatchedAction$isN; if (isInner) { TimeTravel.processing = true; } var syncStackId = materialCallStack.push({ type: type, method: name, domain: domain === null || domain === void 0 ? void 0 : domain.constructor.name }); if (!isInner && type !== EMaterialType.MUTATION && type !== EMaterialType.UPDATE) { return; } var callback = function callback() { var ids = _toConsumableArray(triggerCollector.waitTriggerIds.values()); if (!ids.length && !forceSaveHistory) { clean(false); return; } if (!isInner && isNeedRecord) { triggerCollector.save(); } var computedReactionIds = ids.filter(function (id) { return id instanceof Reaction && id.lazy && id.computed; }); // do lazy computed reaction first,PS:maybe add new trigger ids if (ctx.devTool) { console.log('lazy computed', computedReactionIds); } flushChange(computedReactionIds, false, isInner); var lazyReactiveIds = _toConsumableArray(triggerCollector.waitTriggerIds.values()).filter(function (id) { return id instanceof Reaction && !id.computed && !id.immediately; }); var restIds = _toConsumableArray(triggerCollector.waitTriggerIds.values()).filter(function (id) { return !(id instanceof Reaction); }); clean(); // do lazy reactive if (ctx.devTool) { console.log('lazy reactive', lazyReactiveIds); } flushChange(lazyReactiveIds, false, isInner); // do Reactive/ReactReactive/CustomReactive if (ctx.devTool) { console.log('custom view Reactive', restIds); } flushChange(restIds, true, isInner); }; mutationDepth += 1; if (isInner || immediately && triggerCollector.waitTriggerIds.size > 0) { // flush previous job dirtyJob && dirtyJob(); } if (dirtyJob === void 0) { dirtyJob = callback; } var result; if (ctx.devTool) { var event = { domain: (_a = domain === null || domain === void 0 ? void 0 : domain.constructor.name) !== null && _a !== void 0 ? _a : 'UNKNOWN', method: name, args: payload, state: domain === null || domain === void 0 ? void 0 : domain.$$turboxProperties, time: Date.now(), stackId: (_b = materialCallStack.currentStack) === null || _b === void 0 ? void 0 : _b.stackId }; emitter.emit(EEventName.beforeStoreChange, event); } try { result = original.apply(void 0, _toConsumableArray(payload)); } catch (error) { return error; } if (ctx.devTool) { var _event = { domain: (_c = domain === null || domain === void 0 ? void 0 : domain.constructor.name) !== null && _c !== void 0 ? _c : 'UNKNOWN', method: name, args: payload, state: domain === null || domain === void 0 ? void 0 : domain.$$turboxProperties, async: false, time: Date.now(), stackId: (_d = materialCallStack.currentStack) === null || _d === void 0 ? void 0 : _d.stackId }; emitter.emit(EEventName.afterStoreChange, _event); } if (isPromise(result)) { materialCallStack.pop(syncStackId); return new Promise(function (resolve, reject) { var asyncStackId; Promise.resolve().then(function () { asyncStackId = materialCallStack.push({ type: type, method: name, domain: domain === null || domain === void 0 ? void 0 : domain.constructor.name, syncStackId: syncStackId }); }); result.then(function (res) { var _a, _b; if (ctx.devTool) { emitter.emit(EEventName.asyncAfterStoreChange, { domain: (_a = domain === null || domain === void 0 ? void 0 : domain.constructor.name) !== null && _a !== void 0 ? _a : 'UNKNOWN', method: name, args: payload, state: domain === null || domain === void 0 ? void 0 : domain.$$turboxProperties, async: true, time: Date.now(), stackId: (_b = materialCallStack.currentStack) === null || _b === void 0 ? void 0 : _b.stackId }); } keepAliveComputed(isInner); immediatelyReactive(isInner); mutationDepth -= 1; if (immediately) { // immediately execute dirtyJob && dirtyJob(); } else { nextTickCaller(); } materialCallStack.pop(asyncStackId); resolve(res); })["catch"](function (error) { reject(error); }); }); } keepAliveComputed(isInner); immediatelyReactive(isInner); mutationDepth -= 1; if (isInner || immediately) { // immediately execute dirtyJob && dirtyJob(); TimeTravel.processing = false; } else { nextTickCaller(); } materialCallStack.pop(syncStackId); return result; } return { dispatch: dispatch, subscribe: subscribe }; }