@turbox3d/reactivity
Version:
Large-scale reactive state management library
301 lines • 10.6 kB
JavaScript
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
};
}