resy
Version:
React State Easy
1,102 lines (1,088 loc) • 62.8 kB
JavaScript
import { unstable_batchedUpdates } from './platform.esm';
import useSyncExternalStoreExports from 'use-sync-external-store/shim';
import { useRef, useDebugValue, useEffect, useMemo, Component, PureComponent } from 'react';
function _arrayLikeToArray(r, a) {
(null == a || a > r.length) && (a = r.length);
for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
return n;
}
function _arrayWithHoles(r) {
if (Array.isArray(r)) return r;
}
function _assertThisInitialized(e) {
if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
return e;
}
function _callSuper(t, o, e) {
return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e));
}
function _classCallCheck(a, n) {
if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
}
function _defineProperties(e, r) {
for (var t = 0; t < r.length; t++) {
var o = r[t];
o.enumerable = o.enumerable || false, o.configurable = true, "value" in o && (o.writable = true), Object.defineProperty(e, _toPropertyKey(o.key), o);
}
}
function _createClass(e, r, t) {
return r && _defineProperties(e.prototype, r), Object.defineProperty(e, "prototype", {
writable: false
}), e;
}
function _createForOfIteratorHelper(r, e) {
var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (!t) {
if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e) {
t && (r = t);
var n = 0,
F = function () {};
return {
s: F,
n: function () {
return n >= r.length ? {
done: true
} : {
done: false,
value: r[n++]
};
},
e: function (r) {
throw r;
},
f: F
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var o,
a = true,
u = false;
return {
s: function () {
t = t.call(r);
},
n: function () {
var r = t.next();
return a = r.done, r;
},
e: function (r) {
u = true, o = r;
},
f: function () {
try {
a || null == t.return || t.return();
} finally {
if (u) throw o;
}
}
};
}
function _defineProperty(e, r, t) {
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: true,
configurable: true,
writable: true
}) : e[r] = t, e;
}
function _getPrototypeOf(t) {
return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) {
return t.__proto__ || Object.getPrototypeOf(t);
}, _getPrototypeOf(t);
}
function _inherits(t, e) {
if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function");
t.prototype = Object.create(e && e.prototype, {
constructor: {
value: t,
writable: true,
configurable: true
}
}), Object.defineProperty(t, "prototype", {
writable: false
}), e && _setPrototypeOf(t, e);
}
function _isNativeReflectConstruct() {
try {
var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
} catch (t) {}
return (_isNativeReflectConstruct = function () {
return !!t;
})();
}
function _iterableToArrayLimit(r, l) {
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
if (null != t) {
var e,
n,
i,
u,
a = [],
f = true,
o = false;
try {
if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
} catch (r) {
o = true, n = r;
} finally {
try {
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
} finally {
if (o) throw n;
}
}
return a;
}
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r && (o = o.filter(function (r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})), t.push.apply(t, o);
}
return t;
}
function _objectSpread2(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), true).forEach(function (r) {
_defineProperty(e, r, t[r]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
function _possibleConstructorReturn(t, e) {
if (e && ("object" == typeof e || "function" == typeof e)) return e;
if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined");
return _assertThisInitialized(t);
}
function _setPrototypeOf(t, e) {
return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) {
return t.__proto__ = e, t;
}, _setPrototypeOf(t, e);
}
function _slicedToArray(r, e) {
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
}
function _toPrimitive(t, r) {
if ("object" != typeof t || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r);
if ("object" != typeof i) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == typeof i ? i : i + "";
}
function _unsupportedIterableToArray(r, a) {
if (r) {
if ("string" == typeof r) return _arrayLikeToArray(r, a);
var t = {}.toString.call(r).slice(8, -1);
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
}
}
// A special identifier within the store generated by createStore
var __REGENERATIVE_SYSTEM_KEY__ = Symbol("regenerativeSystemKey");
// The key of useStore
var __USE_STORE_KEY__ = Symbol("useStoreKey");
// The key of store namespace
var __STORE_NAMESPACE__ = Symbol("storeNamespace");
// @ts-ignore
// eslint-disable-next-line import/no-unresolved
var NODE_ENV = process.env.NODE_ENV;
if (!NODE_ENV) console.error("NODE_ENV not set");
var __DEV__ = NODE_ENV === "development";
/** Batch processing safety shim */
var batchUpdateShimRun = function batchUpdateShimRun(fn) {
return fn();
};
var batchUpdate = unstable_batchedUpdates || batchUpdateShimRun;
var hasOwnProperty = Object.prototype.hasOwnProperty;
var typeString = Object.prototype.toString;
/** Identifying what type of data */
var whatsType = function whatsType(value) {
var _type$match;
var type = typeString.call(value);
switch (type) {
case "[object Array]":
return "Array";
case "[object Object]":
return "Object";
case "[object String]":
return "String";
case "[object Number]":
return "Number";
case "[object Boolean]":
return "Boolean";
case "[object Set]":
return "Set";
case "[object Map]":
return "Map";
case "[object Function]":
return "Function";
case "[object Date]":
return "Date";
case "[object Symbol]":
return "Symbol";
case "[object WeakSet]":
return "WeakSet";
case "[object WeakMap]":
return "WeakMap";
case "[object RegExp]":
return "RegExp";
case "[object WeakRef]":
return "WeakRef";
case "[object BigInt]":
return "BigInt";
case "[object Null]":
return "Null";
case "[object Undefined]":
return "Undefined";
case "[object Promise]":
return "Promise";
case "[object AsyncFunction]":
return "AsyncFunction";
case "[object Array Iterator]":
return "ArrayIterator";
case "[object Set Iterator]":
return "SetIterator";
case "[object Map Iterator]":
return "MapIterator";
case "[object FormData]":
return "FormData";
case "[object Blob]":
return "Blob";
case "[object File]":
return "File";
case "[object Error]":
return "Error";
case "[object CustomEvent]":
return "CustomEvent";
case "[object Storage]":
return "Storage";
case "[object WebSocket]":
return "WebSocket";
case "[object ArrayBuffer]":
return "ArrayBuffer";
case "[object DataView]":
return "DataView";
case "[object Uint8Array]":
return "Uint8Array";
case "[object Int8Array]":
return "Int8Array";
case "[object Uint8ClampedArray]":
return "Uint8ClampedArray";
case "[object Int16Array]":
return "Int16Array";
case "[object Uint16Array]":
return "Uint16Array";
case "[object Int32Array]":
return "Int32Array";
case "[object Uint32Array]":
return "Uint32Array";
case "[object Float32Array]":
return "Float32Array";
case "[object Float64Array]":
return "Float64Array";
case "[object BigInt64Array]":
return "BigInt64Array";
case "[object BigUint64Array]":
return "BigUint64Array";
case "[object XMLHttpRequest]":
return "XMLHttpRequest";
case "[object Headers]":
return "Headers";
case "[object Request]":
return "Request";
case "[object Response]":
return "Response";
case "[object Window]":
return "Window";
case "[object Global]":
return "Global";
case "[object global]":
return "Global";
case "[object Arguments]":
return "Arguments";
default:
// Other type, for-example: [object HTMLElement]
return type === null || type === void 0 || (_type$match = type.match(/\[object\s+(.*?)]/)) === null || _type$match === void 0 ? void 0 : _type$match[1];
}
};
// Check whether the store is generated by Resy's createStore
var storeErrorProcessing = function storeErrorProcessing(store, fnName) {
if (__DEV__ && !(store !== null && store !== void 0 && store[__REGENERATIVE_SYSTEM_KEY__])) {
throw new Error("resy's ".concat(fnName, "(...): Expected the 'store' argument should be generated by resy's createStore(...). ") + "Instead received: ".concat(whatsType(store).toLocaleLowerCase(), "."));
}
};
// Error processing of data update parameters type
var stateErrorProcessing = function stateErrorProcessing(params) {
var state = params.state,
fnName = params.fnName,
options = params.options;
var stateType = whatsType(state);
if (__DEV__ && stateType !== "Object") {
var fnNameTemp = fnName !== null && fnName !== void 0 ? fnName : options === null || options === void 0 ? void 0 : options.__functionName__;
throw new Error("resy's ".concat(fnNameTemp, "(...): takes an object of state variables to update or") + " a function which returns an object of state variables." + " Instead received: ".concat(stateType.toLocaleLowerCase()));
}
if (__DEV__ && state !== null && state !== void 0 && state[__REGENERATIVE_SYSTEM_KEY__]) {
console.error("Warning: resy's setState、syncUpdate(...): takes a store of generated by resy's" + " createStore that's has no update significance!");
}
};
// Options configuration type error processing of createStore
var optionsErrorProcessing = function optionsErrorProcessing(options) {
var optionsType = whatsType(options);
var optsExist = optionsType !== "Undefined";
var urType = whatsType(options === null || options === void 0 ? void 0 : options.unmountRestore);
var ucsType = whatsType(options === null || options === void 0 ? void 0 : options.__useConciseState__);
var nsType = whatsType(options === null || options === void 0 ? void 0 : options.namespace);
var emType = whatsType(options === null || options === void 0 ? void 0 : options.__enableMacros__);
var emasType = whatsType(options === null || options === void 0 ? void 0 : options.enableMarcoActionStateful);
if (__DEV__ && (optsExist && optionsType !== "Object" || optsExist && optionsType === "Object" && (urType !== "Boolean" && urType !== "Undefined" || ucsType !== "Boolean" && ucsType !== "Undefined" || nsType !== "String" && nsType !== "Undefined" || emType !== "Boolean" && emType !== "Undefined" || emasType !== "Boolean" && emasType !== "Undefined"))) {
throw new Error("resy's createStore(...): Expected the last optional 'options' argument to be a StoreOptions type params." + " Instead received: ".concat(optionsType.toLocaleLowerCase(), "."));
}
};
// Options configuration type error processing of setOptions
var setOptionsErrorProcessing = function setOptionsErrorProcessing(options) {
var optionsType = whatsType(options);
var urType = whatsType(options === null || options === void 0 ? void 0 : options.unmountRestore);
if (__DEV__ && (optionsType !== "Object" || optionsType === "Object" && urType !== "Boolean")) {
throw new Error("resy's setOptions(...): Expected the 'options' argument to be an object with a unmountRestore attribute." + " Instead received: ".concat(optionsType.toLocaleLowerCase(), "."));
}
};
// Parameter type error processing of subscribe
var subscribeErrorProcessing = function subscribeErrorProcessing(listener, stateKeys) {
var listenerType = whatsType(listener);
var stateKeysType = whatsType(stateKeys);
if (__DEV__ && listenerType !== "Function") {
throw new Error("resy's subscribe(...): Expected the first optional 'listener' argument to be a function. " + "Instead received: ".concat(listenerType.toLocaleLowerCase(), "."));
}
if (__DEV__ && stateKeysType !== "Undefined" && stateKeysType !== "Array") {
throw new Error("resy's subscribe(...): Expected the last optional 'stateKeys' argument to be a array. " + "Instead received: ".concat(stateKeysType.toLocaleLowerCase(), "."));
}
};
// Callback function type error processing
var stateCallbackErrorProcessing = function stateCallbackErrorProcessing(callback) {
var callbackType = whatsType(callback);
if (__DEV__ && callbackType !== "Undefined" && callbackType !== "Function") {
throw new Error("resy's stateCallback(...): Expected the last optional 'callback' argument to be a function. " + "Instead received: ".concat(callbackType.toLocaleLowerCase(), "."));
}
};
/**
* @description It's not recommended to set the store created by createStore as the prototype of some objects,
* as the store internally hijacks the binding of this context for function properties
* (which does not affect arrow functions).
* This means that for the inherited properties of those set objects,
* the direction of this will be changed to the hijacked this object specified within the store.
* The issues caused by such hijacking of this binding also extend to property descriptors like setters and getters.
* Since the hijacking of this binding for function properties deviates from the native behavior,
* this could lead to confusion.
* Therefore, such operations are discouraged!
*/
var protoPointStoreErrorProcessing = function protoPointStoreErrorProcessing(receiver, store) {
if (__DEV__ && receiver !== store) {
console.error("Warning: It's not recommended to set the store created by createStore as the prototype of some objects" + " as the store internally hijacks the binding of this context for function properties!");
}
};
/** Track the shallow-cloned state map */
var shallowCloneMap = function shallowCloneMap(map) {
var mapTemp = new Map();
map.forEach(function (value, key) {
mapTemp.set(key, value);
});
return mapTemp;
};
/** map to object */
var mapToObject = function mapToObject(map) {
var object = {};
var _iterator = _createForOfIteratorHelper(map),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _step$value = _slicedToArray(_step.value, 2),
key = _step$value[0],
value = _step$value[1];
object[key] = value;
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return object;
};
/** object to map */
var objectToMap = function objectToMap(object) {
return Object.keys(object).reduce(function (prev, key) {
prev.set(key, object[key]);
return prev;
}, new Map());
};
/**
* @description Determine whether the current change data is within the monitoring range of stateKeys
* @return boolean
*/
var effectStateInListenerKeys = function effectStateInListenerKeys(effectState, stateKeys) {
var effectExecFlag = false;
var listenerKeysExist = stateKeys && (stateKeys === null || stateKeys === void 0 ? void 0 : stateKeys.length) > 0;
/**
* @description In fact, when the final subscription is triggered,
* each of these outer layer listenerWraps subscribed is activated.
* It's just that here, the execution of the inner listener is contingent upon a data change check,
* which then determines whether the listener in subscribe should be executed.
*/
if (listenerKeysExist && Object.keys(effectState).some(function (key) {
return stateKeys.includes(key);
}) || !listenerKeysExist) {
effectExecFlag = true;
}
return effectExecFlag;
};
/**
* @description Each store's dispatch handler
* Since the task queue or data inside the dispatcher is operated based on keys,
* and different stores may have the same data attributes,
* so it is necessary to privatize each store's dispatch handler.
* However, the update dispatch of different stores does not affect the triggering of batch updates,
* because the data state in resy has been pre-synchronous execution.
* Therefore, although subsequent store updates are not actually be executed,
* they will incidentally synchronize the data in the store with the rendering on the page.
* This saves one update effect and can be considered a batch update handling across different stores.
* At present, the effect of this characteristic is good, keep observation.
*/
var scheduler = function scheduler() {
// task data of updated
var taskDataMap = new Map();
// task queue of updated
var taskQueueMap = new Map();
// Callback function stack
var callbackStackSet = new Set();
// Scheduling map for batch updates
var schedulerProcessor = new Map();
schedulerProcessor.set("isUpdating", null);
schedulerProcessor.set("willUpdating", null);
schedulerProcessor.set("deferEffectDestructorExecFlag", null);
schedulerProcessor.set("pushTask", function (key, value, task) {
taskDataMap.set(key, value);
taskQueueMap.set(key, task);
});
schedulerProcessor.set("pushCallbackStack", function (stateMap, state, callback) {
if (callback !== undefined) {
stateCallbackErrorProcessing(callback);
var nextState = Object.assign({}, mapToObject(stateMap), state);
callbackStackSet.add({
nextState: nextState,
callback: callback
});
}
});
schedulerProcessor.set("flushTask", function () {
taskDataMap.clear();
taskQueueMap.clear();
});
schedulerProcessor.set("getSchedulerQueue", function () {
return {
taskDataMap: taskDataMap,
taskQueueMap: taskQueueMap,
callbackStackSet: callbackStackSet
};
});
return schedulerProcessor;
};
// The key is for class component connects to store
var __CLASS_CONNECT_STORE_KEY__ = Symbol("classConnectStoreKey");
// The key of the set collection of store used internally by the class component
var __CLASS_THIS_POINTER_STORES_KEY__ = Symbol("classThisPointerStoresKey");
// The key is for data references of class component
var __CLASS_STATE_REF_SET_KEY__ = Symbol("classStateRefSetKey");
// The key is for unmountProcessing of class component
var __CLASS_UNMOUNT_PROCESSING_KEY__ = Symbol("classUnmountProcessingKey");
// The key is for initialStateRetrieve of class component
var __CLASS_INITIAL_STATE_RETRIEVE_KEY__ = Symbol("classInitialStateRetrieveKey");
/** clear object */
var clearObject = function clearObject(object) {
Object.keys(object).forEach(function (key) {
delete object[key];
});
};
/**
* @description Get all the properties
* Here we merge the data attributes of the current "stateMap" and the initial "reducerState"
* in order to count all the new or deleted attributes.
* It is convenient to use the hasOwnProperty method
* to check whether the 'reducerState' has a specific data attribute before restoring the data.。
* Thinking backwards,
* if we don't aggregate all the keys,
* then we can only perform the traversal of keys based on either 'reducerState' or 'stateMap',
* and restore them based on whether they have properties confirmed by the hasOwnProperty method.
* If we choose reducerState, we will not be able to control the newly added key,
* and if we choose stateMap, we will not be able to delete the key.
* Neither of them is perfect, so we must merge both sets of results.
*/
var mergeStateKeys = function mergeStateKeys(reducerState, stateMap) {
return Array.from(new Set(Object.keys(reducerState).concat(Array.from(stateMap.keys()))));
};
/**
* Retrieve the reducerState
* @description If the data is in the initialization state and returned by a function,
* the initialization function must be executed again.
* This ensures that the retrieved internal initialization data aligns with the function's logic.
* For example, if the initialization function's return includes time in milliseconds,
* it is important to re-execute the function to acquire the most up-to-date initialization data.
* Such caution ensures the precision of data recovery.
*/
var retrieveReducerState = function retrieveReducerState(reducerState, initialState) {
if (typeof initialState === "function") {
clearObject(reducerState);
Object.entries(initialState()).forEach(function (_ref) {
var _ref2 = _slicedToArray(_ref, 2),
key = _ref2[0],
value = _ref2[1];
reducerState[key] = value;
});
}
};
// Logic of recovery processing
var restoreProcessing = function restoreProcessing(reducerState, stateMap, initialState) {
retrieveReducerState(reducerState, initialState);
mergeStateKeys(reducerState, stateMap).forEach(function (key) {
hasOwnProperty.call(reducerState, key) ? stateMap.set(key, reducerState[key]) : stateMap["delete"](key);
});
};
/**
* By using "storeStateRefCounterMap" and "classThisPointerSet",
* we determine whether the store still has component references.
* As long as there is at least one component referencing,
* the data will not be reset since it is currently in use within the business logic and does not constitute a complete unmount.
* The complete unmount cycle corresponds to the entire usage cycle of the store.
*/
var unmountRestore = function unmountRestore(options, reducerState, stateMap, storeStateRefCounterMap, initialFnCanExecMap, classThisPointerSet, initialState) {
var noRefFlag = !classThisPointerSet.size && !storeStateRefCounterMap.get("counter");
/**
* When initialState is a function,
* it does not have to be executed at unmount time,
* because initialization time is sure to reset execution,
* thus optimizing code execution efficiency.
*/
if (options.unmountRestore && noRefFlag && typeof initialState !== "function") {
restoreProcessing(reducerState, stateMap, initialState);
}
if (typeof initialState === "function" && noRefFlag) {
initialFnCanExecMap.set("canExec", true);
}
};
// Retrieve recovery processing when initialState is a function
var initialStateRetrieve = function initialStateRetrieve(reducerState, stateMap, initialFnCanExecMap, initialState) {
// The relevant judgment logic is similar to unmountRestore.
if (initialFnCanExecMap.get("canExec")) {
initialFnCanExecMap.set("canExec", null);
restoreProcessing(reducerState, stateMap, initialState);
}
};
/**
* @description In order to prevent the double rendering in React's StrictMode
* from causing issues with the registration function returned in useEffect,
* it happens to be opportune for storeMap to release memory preemptively
* during the first unmount execution.
* (with memory release being performed in the callback).
* This early release of memory removes the previous storeMapValue,
* and any subsequent updates or renderings will regenerate a new storeMapValue.
* However, this process leads to the updater function's singlePropStoreChangeSet
* within storeMapValue referencing the address of the previously outdated storeMapValue.
* Meanwhile, that old singlePropStoreChangeSet has already been deleted.
* and cleared with the early release of the storeMapValue's memory,
* leading to the updater function's incapability to make valid updates.
* Here, to ensure operations such as unmount, freeing memory,
* and unmountRestore run smoothly,
* a microtask can be used to postpone the unmount process.
*/
function deferRestoreProcessing(options, reducerState, stateMap, storeStateRefCounterMap, schedulerProcessor, initialFnCanExecMap, classThisPointerSet, initialState, callback) {
if (!schedulerProcessor.get("deferEffectDestructorExecFlag")) {
schedulerProcessor.set("deferEffectDestructorExecFlag", Promise.resolve().then(function () {
schedulerProcessor.set("deferEffectDestructorExecFlag", null);
if (!storeStateRefCounterMap.get("counter") && !classThisPointerSet.size) {
unmountRestore(options, reducerState, stateMap, storeStateRefCounterMap, initialFnCanExecMap, classThisPointerSet, initialState);
}
callback === null || callback === void 0 || callback();
}));
}
}
/**
* @description Additional references are utilized to ensure the compatibility
* of the package in ESM since 'use-sync-external-store' only exports in CJS format.
*/
var useSyncExternalStore = useSyncExternalStoreExports.useSyncExternalStore;
/**
* @description By connecting the core mapping variable storeMap within the store,
* a series of operations such as tagging, subscribing, updating, tracking, and rendering are completed.
*/
var connectStore = function connectStore(key, options, reducerState, stateMap, storeStateRefCounterMap, storeMap, schedulerProcessor, initialFnCanExecMap, classThisPointerSet, initialState) {
// Resolve the problem that the initialization attribute may be undefined
if (storeMap.has(key)) return storeMap;
var storeMapValue = new Map();
// The Set memory of the update function of a single attribute
var singleStoreChangeSet = new Set();
storeMapValue.set("subscribe", function (onStoreChange) {
// If a component references the data, the update function will be added to singleStoreChangeSet
singleStoreChangeSet.add(onStoreChange);
// Increment the reference count by 1 if the component is referenced
storeStateRefCounterMap.set("counter", storeStateRefCounterMap.get("counter") + 1);
return function () {
singleStoreChangeSet["delete"](onStoreChange);
storeStateRefCounterMap.set("counter", storeStateRefCounterMap.get("counter") - 1);
deferRestoreProcessing(options, reducerState, stateMap, storeStateRefCounterMap, schedulerProcessor, initialFnCanExecMap, classThisPointerSet, initialState, function () {
// Release memory if there are no component references
if (!singleStoreChangeSet.size) {
storeMap["delete"](key);
}
});
};
});
storeMapValue.set("getSnapshot", function () {
return stateMap.get(key);
});
storeMapValue.set("useSyncExternalStore", function () {
return useSyncExternalStore(storeMap.get(key).get("subscribe"), storeMap.get(key).get("getSnapshot"), storeMap.get(key).get("getSnapshot"));
});
storeMapValue.set("updater", function () {
singleStoreChangeSet.forEach(function (stateChange) {
stateChange();
});
});
storeMap.set(key, storeMapValue);
return storeMap;
};
/**
* @description It is essentially a call to useSyncExternalStore,
* concatenating a series of operations through the core storeMap generated in connectStore.
*/
var connectHook = function connectHook(key, options, reducerState, stateMap, storeStateRefCounterMap, storeMap, schedulerProcessor, initialFnCanExecMap, classThisPointerSet, initialState) {
// Perform refresh recovery logic if initialState is a function
initialStateRetrieve(reducerState, stateMap, initialFnCanExecMap, initialState);
return connectStore(key, options, reducerState, stateMap, storeStateRefCounterMap, storeMap, schedulerProcessor, initialFnCanExecMap, classThisPointerSet, initialState).get(key).get("useSyncExternalStore")();
};
/**
* @description The corresponding connectHook and connectClass are for class components,
* but they only need to make the corresponding tags and return the data.
*/
function connectClass(key, stateMap) {
// In class, Set is used for reference tags and combined with the size attribute of Set to judge.
this[__CLASS_STATE_REF_SET_KEY__].add(key);
return stateMap.get(key);
}
// State updates for class components
var classUpdater = function classUpdater(key, value, classThisPointerSet) {
classThisPointerSet === null || classThisPointerSet === void 0 || classThisPointerSet.forEach(function (classThisPointerItem) {
/**
* There is an "updater" attribute on the internal this pointer of react's class,
* and an "isMounted" method is mounted on it to determine whether the component has been loaded.
* If it is in "React.StrictMode" mode,
* React will discard the first generated instance and the instance will not be mounted.
*/
if (classThisPointerItem.updater.isMounted(classThisPointerItem)) {
var _classThisPointerItem;
/**
* @description Determine whether the currently updated data property
* is used in the class component, and if not, do not update it.
* 🌟 Don't worry about the use of hidden attributes caused by operations such as ternary operators.
* Even the use of hidden attributes here will not cause rendering problems,
* because the state attribute reference of the class component does not have a hook rule.
* At the same time, when a hidden attribute is discovered by a new rendering,
* it will immediately generate a new state attribute reference.
* Therefore, this is always safe, and it can avoid unnecessary re-renders.
* 🌟 Adding "?.has" is to prevent some class components from making an empty connection,
* that is, connecting to the store but not using it. Generally speaking, this is not done,
*/
((_classThisPointerItem = classThisPointerItem[__CLASS_STATE_REF_SET_KEY__]) === null || _classThisPointerItem === void 0 ? void 0 : _classThisPointerItem.has(key)) && classThisPointerItem.setState(_defineProperty({}, key, value));
} else {
classThisPointerSet["delete"](classThisPointerItem);
}
});
};
// Add the update task to the stack
var pushTask = function pushTask(key, value, stateMap, schedulerProcessor, options, reducerState, storeStateRefCounterMap, storeMap, initialFnCanExecMap, classThisPointerSet, initialState, isDelete) {
// The pre-execution of the data changes accumulates the logic of the correct execution of the final update,
// which lays the foundation for subsequent batch updates.
!isDelete ? stateMap.set(key, value) : stateMap["delete"](key);
schedulerProcessor.get("pushTask")(key, value, function () {
classUpdater(key, value, classThisPointerSet);
/**
* @description The decision not to execute the updates for class components within the following updater
* is to preserve the simplicity of the update scheduling for both hook and class components.
*/
// Status updates for hook components
connectStore(key, options, reducerState, stateMap, storeStateRefCounterMap, storeMap, schedulerProcessor, initialFnCanExecMap, classThisPointerSet, initialState).get(key).get("updater")();
});
};
/**
* @description Final batch update processing
* With the help of the micro-task event loop via 'then',
* the update execution of data and tasks are placed onto the stack, and subsequently flushed.
*/
var finallyBatchProcessing = function finallyBatchProcessing(schedulerProcessor, prevBatchState, stateMap, listenerSet) {
var _schedulerProcessor$g = schedulerProcessor.get("getSchedulerQueue")(),
taskDataMap = _schedulerProcessor$g.taskDataMap,
taskQueueMap = _schedulerProcessor$g.taskQueueMap,
callbackStackSet = _schedulerProcessor$g.callbackStackSet;
if ((taskDataMap.size > 0 || callbackStackSet.size > 0) && !schedulerProcessor.get("isUpdating")) {
// Reduce the generation of redundant microtasks through the isUpdating flag
schedulerProcessor.set("isUpdating", Promise.resolve().then(function () {
/**
* @description Reset the isUpdating and willUpdating flags
* to ensure that each subsequent round of update batching can proceed and operate normally.
*/
schedulerProcessor.set("isUpdating", null);
schedulerProcessor.set("willUpdating", null);
batchUpdate(function () {
if (taskDataMap.size > 0) {
// Perform update task
taskQueueMap.forEach(function (task) {
task();
});
}
// Make a shallow clone of the "taskDataMap" data for the "effectState" of "subscribe",
// Perform a shallowClone before executing flushTask, otherwise, it might become impossible to retrieve `taskDataMap`.
var effectStateTemp = listenerSet.size > 0 ? shallowCloneMap(taskDataMap) : undefined;
/**
* @description So far, the task of this round of data updates is complete.
* The task data and task queue are immediately flushed and cleared,
* freeing up space in preparation for the next round of data updates.
*/
schedulerProcessor.get("flushTask")();
// 🌟 The execution of subscribe and callback needs to be placed after flush,
// otherwise their own update queues will be emptied in advance, affecting their own internal execution.
// Trigger the execution of the callback function
if (callbackStackSet.size > 0) {
callbackStackSet.forEach(function (_ref) {
var callback = _ref.callback,
nextState = _ref.nextState;
callback(nextState);
});
callbackStackSet.clear();
}
// 🌟 As logically, the listener in subscribe needs to be executed after the callback has been executed.
// Trigger the execution of subscription snooping
if (listenerSet.size > 0) {
// Reduce the burden of executing `mapToObject` on three data sets through proxy.
var listenerDataProxy = new Proxy({}, {
get: function get(_, listenerDataKey) {
if (listenerDataKey === "effectState") {
return mapToObject(effectStateTemp);
}
if (listenerDataKey === "nextState") {
return mapToObject(stateMap);
}
if (listenerDataKey === "prevState") {
return mapToObject(prevBatchState);
}
}
});
listenerSet.forEach(function (item) {
// the clone returned by mapToObject ensures that the externally subscribed data
// maintains it`s purity and security as much as possible in terms of usage.
item(listenerDataProxy);
});
}
});
}));
}
};
/**
* @description Hook of subscribe
* It`s advantage is that you only need to consider the data you want to subscribe to,
* rather than the psychological burden to consider whether the data reference inside the function can get the latest value.
* UseSubscription will reduce your mental burden and allow you to use it normally.
*/
var useSubscription = function useSubscription(store, listener, stateKeys) {
storeErrorProcessing(store, "useSubscription");
subscribeErrorProcessing(listener, stateKeys);
var ref = useRef(null);
/**
* The ref writing here essentially does not affect the rules of React pure functions.
* @description stateKeys is generally stable,
* and it is not recommended to use scenarios with changes in stateKeys,
* but the use of complex scenarios is still considered here
*/
ref.current = {
listener: listener,
stateKeys: stateKeys
};
if (__DEV__) {
var store_namespace = store[__STORE_NAMESPACE__] ? {
namespace: store[__STORE_NAMESPACE__]
} : null;
// eslint-disable-next-line react-hooks/rules-of-hooks
useDebugValue(_objectSpread2({
listener: listener,
stateKeys: stateKeys
}, store_namespace));
}
useEffect(function () {
// Monitor the overall data changes of the store
return store.subscribe(function (data) {
/**
* @description First determine whether there is a change in execution,
* and if so, delay the execution in order to get the latest listening subscription function
* and the array of listening data attributes given by useMemo.
*/
if (effectStateInListenerKeys(data.effectState, ref.current.stateKeys)) {
// Delay execution in order to get the new listener function after re-rendering
Promise.resolve(data).then(function (res) {
ref.current.listener(res);
});
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
};
// Pre-update processing
// records the prevBatchState beforehand for later comparison when data changes trigger subscribers
var willUpdatingProcessing = function willUpdatingProcessing(listenerSet, schedulerProcessor, prevBatchState, stateMap) {
if (listenerSet.size > 0 && !schedulerProcessor.get("willUpdating")) {
schedulerProcessor.set("willUpdating", true);
// Clear first to prevent store from having delete operations that cause prevBatchState to retain deleted data
prevBatchState.clear();
stateMap.forEach(function (value, key) {
prevBatchState.set(key, value);
});
}
};
/**
* createStore
* created by liushanbao
* @description Create a state storage container that can be used globally
* @author liushanbao
* @date 2022-05-05
* @param initialState
* @param options
* @return Store<S>
*/
var _createStore = function createStore(initialState, options) {
var _options$__useConcise, _options$unmountResto, _options$namespace, _options$__enableMacr, _options$enableMarcoA, _options$__functionNa;
/** ============================== For core constant ready start ============================== */
// Retrieve the reducerState
var reducerState = initialState === undefined ? {} : typeof initialState === "function" ? initialState() : initialState;
optionsErrorProcessing(options);
var optionsTemp = {
__useConciseState__: (_options$__useConcise = options === null || options === void 0 ? void 0 : options.__useConciseState__) !== null && _options$__useConcise !== void 0 ? _options$__useConcise : undefined,
unmountRestore: (_options$unmountResto = options === null || options === void 0 ? void 0 : options.unmountRestore) !== null && _options$unmountResto !== void 0 ? _options$unmountResto : true,
namespace: (_options$namespace = options === null || options === void 0 ? void 0 : options.namespace) !== null && _options$namespace !== void 0 ? _options$namespace : undefined,
__enableMacros__: (_options$__enableMacr = options === null || options === void 0 ? void 0 : options.__enableMacros__) !== null && _options$__enableMacr !== void 0 ? _options$__enableMacr : undefined,
enableMarcoActionStateful: (_options$enableMarcoA = options === null || options === void 0 ? void 0 : options.enableMarcoActionStateful) !== null && _options$enableMarcoA !== void 0 ? _options$enableMarcoA : undefined,
__functionName__: (_options$__functionNa = options === null || options === void 0 ? void 0 : options.__functionName__) !== null && _options$__functionNa !== void 0 ? _options$__functionNa : _createStore.name
};
stateErrorProcessing({
state: reducerState,
options: optionsTemp
});
var schedulerProcessor = scheduler();
// Tag counters for data references of store
var storeStateRefCounterMap = new Map().set("counter", 0);
// Flag indicating that the initialStateRetrieve function is executable
var initialFnCanExecMap = new Map();
/**
* @description Use Map and Set to improve performance,
* "Simultaneously, it can keep the `initialState` unchanged."
*/
var stateMap = objectToMap(reducerState);
// Data status of the previous update batch
var prevBatchState = objectToMap(reducerState);
// Subscription listener stack
var listenerSet = new Set();
// The core map of store
var storeMap = new Map();
// The storage stack of this proxy object for the class component
var classThisPointerSet = new Set();
/**
* @description Map for additional related internal objects of store
* For example, some related functions or identifiers,
* such as setState, subscribe and internal identity __REGENERATIVE_SYSTEM_KEY__
*/
var externalMap = new Map();
/** ============================== For core constant ready end ============================== */
// for development tools
externalMap.set(__STORE_NAMESPACE__, optionsTemp.namespace);
/** ============================== For core utils use start ============================== */
var setState = function setState(state, callback) {
willUpdatingProcessing(listenerSet, schedulerProcessor, prevBatchState, stateMap);
var stateTemp = state;
if (typeof state === "function") {
// processing of prevState
stateTemp = state(mapToObject(stateMap));
}
if (stateTemp !== null) {
stateErrorProcessing({
state: stateTemp,
fnName: "setState、syncUpdate"
});
// The update of hook is an independent update dispatch action, and traversal processing is needed to unify the stack.
Object.keys(stateTemp).forEach(function (key) {
var value = stateTemp[key];
if (!Object.is(value, stateMap.get(key))) {
pushTask(key, value, stateMap, schedulerProcessor, optionsTemp, reducerState, storeStateRefCounterMap, storeMap, initialFnCanExecMap, classThisPointerSet, initialState);
}
});
}
schedulerProcessor.get("pushCallbackStack")(stateMap, stateTemp, callback);
finallyBatchProcessing(schedulerProcessor, prevBatchState, stateMap, listenerSet);
};
/**
* @description syncUpdate primarily exists to address issues with normal text input.
* to meet the needs of normal text input, it synchronizes React's update scheduling.
*/
var syncUpdate = function syncUpdate(state, callback) {
var stateTemp = state;
if (typeof state === "function") {
stateTemp = state(mapToObject(stateMap));
}
// Borrowing setState to synchronize the update scheduling mechanism of Resy itself.
setState(stateTemp, callback);
if (stateTemp !== null) {
batchUpdate(function () {
Object.keys(stateTemp).forEach(function (key) {
var value = stateTemp[key];
classUpdater(key, value, classThisPointerSet);
connectStore(key, optionsTemp, reducerState, stateMap, storeStateRefCounterMap, storeMap, schedulerProcessor, initialFnCanExecMap, classThisPointerSet, initialState).get(key).get("updater")();
});
});
}
};
// Reset recovery initialization state data
var restore = function restore(callback) {
willUpdatingProcessing(listenerSet, schedulerProcessor, prevBatchState, stateMap);
retrieveReducerState(reducerState, initialState);
var state = {};
mergeStateKeys(reducerState, stateMap).forEach(function (key) {
var originValue = reducerState[key];
if (!Object.is(originValue, stateMap.get(key))) {
state[key] = originValue;
pushTask(key, originValue, stateMap, schedulerProcessor, optionsTemp, reducerState, storeStateRefCounterMap, storeMap, initialFnCanExecMap, classThisPointerSet, initialState, !hasOwnProperty.call(reducerState, key));
}
});
schedulerProcessor.get("pushCallbackStack")(stateMap, state, callback);
finallyBatchProcessing(schedulerProcessor, prevBatchState, stateMap, listenerSet);
};
// Subscription function
var subscribe = function subscribe(listener, stateKeys) {
subscribeErrorProcessing(listener, stateKeys);
var listenerWrap = function listenerWrap(data) {
if (effectStateInListenerKeys(data.effectState, stateKeys)) listener(data);
};
listenerSet.add(listenerWrap);
// Returns the unsubscribing function, which allows the user to choose whether or not to unsubscribe,
// because it is also possible that the user wants the subscription to remain in effect.
return function () {
return listenerSet["delete"](listenerWrap);
};
};
externalMap.set("setState", setState);
externalMap.set("syncUpdate", syncUpdate);
externalMap.set("restore", restore);
externalMap.set("subscribe", subscribe);
/** ============================== For core utils use end ============================== */
/** ============================== For core render use start ============================== */
// Data updates for a single attribute
var singleUpdate = function singleUpdate(key, value, isDelete) {
if (!Object.is(value, stateMap.get(key))) {
willUpdatingProcessing(listenerSet, schedulerProcessor, prevBatchState, stateMap);
pushTask(key, value, stateMap, schedulerProcessor, optionsTemp, reducerState, storeStateRefCounterMap, storeMap, initialFnCanExecMap, classThisPointerSet, initialState, isDelete);
finallyBatchProcessing(schedulerProcessor, prevBatchState, stateMap, listenerSet);
}
return true;
};
// Updated handler configuration for proxy
var proxySetHandler = {
set: function set(_, key, value) {
return singleUpdate(key, value);
},
// Delete will also play an updating role
deleteProperty: function deleteProperty(_, key) {
return singleUpdate(key, undefined, true);
}
};
var macroFnProcessing = function macroFnProcessing(key, value) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
var boundFn = function boundFn() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return value.apply(store, args);
};
boundFn.__bound__ = true;
stateMap.set(key, boundFn);
return boundFn;
};
// A proxy object with the capabilities of updating and data tracking.
var store = new Proxy(stateMap, _objectSpread2({
get: function get(_, key, receiver) {
protoPointStoreErrorProcessing(receiver, store);
var value = stateMap.get(key);
if (typeof value === "function" && !value.__bound__) {
return macroFnProcessing(key, value);
}
return externalMap.get(key) || value;
}
}, proxySetHandler));
// Proxy of driver update re-render for useStore
var engineStore = new Proxy(stateMap, _objectSpread2({
get: function get(_, key) {
// Get the latest value
var value = stateMap.get(key);
var notExternal = !externalMap.has(key);
if (notExternal && typeof value !== "function") {
// eslint-disable-next-line react-hooks/rules-of-hooks
__DEV__ && useDebugValue(_objectSpread2({
key: key,
value: value
}, optionsTemp.namespace ? {
namespace: optionsTemp.namespace
} : null));
return connectHook(key, optionsTemp, reducerState, stateMap, storeStateRefCounterMap, storeMap, schedulerProcessor, initialFnCanExecMap, classThisPointerSet, initialState);
}
if (notExternal && typeof value === "function") {
// Avoid memory redundancy waste caused by repeated bindings and maintain the function reference address unchanged.
if (!value.__bound__) {
macroFnProcessing(key, value);
}
var fnStateful = !optionsTemp.__enableMacros__ || optionsTemp.enableMarcoActionStateful;
var newValue = stateMap.get(key);
// eslint-disable-next-line react-hooks/rules-of-hooks
fnStateful && __DEV__ && useDebugValue(_objectSpread2({
key: key,
value: newValue
}, optionsTemp.namespace ? {
namespace: optionsTemp.namespace
} : null));
/**
* @description Enable function properties to have the ability to update rendering.
* Placing both the __bound__ and the stateMap's set operation before the connectHook
* can preemptively avoid the tearing synchronization handling inside useSyncExternalStore,
* resulting in twice the redundant rendering execution.
*/
fnStateful && connectHook(key, optionsTemp, reducerState, stateMap, storeStateRefCounterMap, storeMap, schedulerProcessor, initialFnCanExecMap, classThisPointerSet, initialState);
return newValue;
}
return externalMap.get(key);
}
}, proxySetHandler));
// Enable useConciseState and defineStore to have data tracking capabilities through the store
if (optionsTemp.__useConciseState__ || optionsTemp.__enableMacros__) {
externalMap.set("store", store);
}
/**
* @description Here, __USE_STORE_KEY__ is not placed under the branch where optionsTemp.__mode__ === "state",
* because computed needs the rendering capability of engineStore.
*/
externalMap.set(__USE_S