UNPKG

resy

Version:
1,102 lines (1,088 loc) 62.8 kB
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