UNPKG

redext

Version:

A simple global store based on React Context and Hooks

536 lines (510 loc) 16.4 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { Provider: () => Provider_default, connect: () => connect_default, deepEqual: () => deepEqual, init: () => init, isEqual: () => exportedEqual, memoize: () => memoize_default, useDeepCallback: () => useDeepCallback_default, useDeepEffect: () => useDeepEffect_default, useDeepIsomorphicLayoutEffect: () => useDeepIsomorphicLayoutEffect_default, useDeepMemo: () => useDeepMemo_default, useDispatch: () => useDispatch, useDispatcher: () => useDispatcher_default, useSelector: () => useSelector_default }); module.exports = __toCommonJS(index_exports); // src/Provider.tsx var import_react2 = require("react"); // src/Context.tsx var import_react = require("react"); var Context_default = (0, import_react.createContext)({}); // src/Provider.tsx var import_jsx_runtime = require("react/jsx-runtime"); var Provider = (props) => { const { store, initialValue, ...providerProps } = props; if (!store) { throw new Error("Please use <Provider store={...} initialValue={...}>"); } const listeners = /* @__PURE__ */ new Set(); const initialState = store.getState(initialValue); const [state, dispatch] = (0, import_react2.useReducer)(store.getReducer, initialState); const stateRef = (0, import_react2.useRef)(initialState); stateRef.current = state; const getState = () => { return stateRef.current; }; const { effects, dispatch: dispatcher, models, on } = store.getEffect(dispatch, state); const subscribe = (listener) => { listeners.add(listener); return () => { listeners.delete(listener); }; }; const value = { subscribe, getState, state: getState(), dispatch: (arg) => { on("onModel", (onModel) => { var _a; const type = arg == null ? void 0 : arg.type; const types = (_a = type == null ? void 0 : type.split) == null ? void 0 : _a.call(type, "/"); const modelName = types == null ? void 0 : types[0]; if (!modelName) { return; } const actionName = types == null ? void 0 : types[1]; const model = models[modelName]; onModel({ model: { ...model, name: modelName }, modelName, actionName, dispatch: dispatcher }); }); dispatcher(arg); listeners.forEach((listener) => listener()); }, effects }; return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( Context_default.Provider, { value, ...providerProps } ); }; var Provider_default = Provider; // src/createStore.ts var merge = (original, extra) => { return extra ? { ...extra, ...original } : original; }; function createStore(config) { const { plugins = [] } = config; let models = config.models; plugins.forEach((plugin) => { if (plugin.config) { models = merge(models, plugin.config.models); } }); const on = (eventName, callback) => { plugins.forEach((plugin) => { if (plugin[eventName]) { callback(plugin[eventName]); } }); }; const getState = (initialState = {}) => { const newInitialState = {}; Object.keys(models).forEach((modelFilename) => { const model = models[modelFilename] || {}; const modelName = model.name || modelFilename; newInitialState[modelName] = Object.assign({}, model.state, initialState[modelName]); }); return newInitialState; }; const getReducer = (state = {}, action = {}) => { const newState = {}; Object.keys(models).forEach((modelFilename) => { var _a, _b; const model = models[modelFilename] || {}; const { reducers = {}, name: modelName = modelFilename } = model; let reducerState = state[modelName]; const actionType = (_b = (_a = action.type) == null ? void 0 : _a.replace) == null ? void 0 : _b.call(_a, `${modelName}/`, ""); if (actionType && actionType in reducers) { reducerState = reducers[actionType](reducerState, action.payload, action.params); } newState[modelName] = reducerState; }); return newState; }; const getEffect = (dispatch, state = {}) => { const newEffects = {}; Object.keys(models).forEach((modelFilename) => { const modelDispatcher = {}; const model = models[modelFilename] || {}; const { reducers = {}, effects: effectsFromConfig, name: modelName = modelFilename } = model; modelDispatcher.state = state[modelName]; const onModelListener = ({ actionName }) => { on("onModel", (onModel) => { onModel({ model: { ...model, name: modelName }, modelName, actionName, dispatch }); }); }; Object.keys(reducers).forEach((actionName) => { const type = `${modelName}/${actionName}`; modelDispatcher[actionName] = (payload, params) => { dispatch({ type, payload, params }); }; }); let effects; dispatch[modelName] = modelDispatcher; if (typeof effectsFromConfig === "function") { effects = effectsFromConfig(dispatch); } else { effects = effectsFromConfig || {}; } const effectObj = {}; Object.keys(effects).forEach((effectName) => { const effectFunc = (...args) => { onModelListener({ actionName: effectName }); return effects[effectName].apply(modelDispatcher, args); }; modelDispatcher[effectName] = effectFunc; effectObj[effectName] = effectFunc; }); newEffects[modelName] = effectObj; }); return { effects: newEffects, models, dispatch, on }; }; return { getState, getReducer, getEffect }; } // src/connect.tsx var import_react7 = require("react"); // src/hooks/useContextSelector.ts var import_react5 = require("react"); // src/hooks/useDeepMemo.ts var import_react4 = __toESM(require("react")); // src/hooks/useDeepMemoize.ts var import_react3 = require("react"); // src/utils/deepEqual.ts var equal = (a, b) => { let ctor; let len; if (a === b) { return true; } if (a && b && (ctor = a.constructor) === b.constructor) { if (ctor === Date) { return a.getTime() === b.getTime(); } if (ctor === RegExp) { return a.toString() === b.toString(); } if (ctor === Array && (len = a.length) === b.length) { while (len-- && equal(a[len], b[len])) ; return len === -1; } if (ctor === Object) { if (Object.keys(a).length !== Object.keys(b).length) { return false; } for (len in a) { if (!(len in b) || !equal(a[len], b[len])) { return false; } } return true; } } return a !== a && b !== b; }; function deepEqual(a, b) { try { return equal(a, b); } catch (error) { if (error.message && error.message.match(/stack|recursion/i) || error.number === -2146828260) { console.warn("Warning: deepEqual does not handle circular references.", error.name, error.message); return false; } throw error; } } // src/hooks/useDeepMemoize.ts var useDeepMemoize = (value) => { const ref = (0, import_react3.useRef)([]); if (!deepEqual(value, ref.current)) { ref.current = value; } return ref.current; }; var useDeepMemoize_default = useDeepMemoize; // src/utils/checkDeps.ts var isPrimitive = (val) => { return val == null || /^[sbn]/.test(typeof val); }; var checkDeps = (deps, name) => { const hookName = `React.${name.replace(/Deep/, "")}`; if (!deps || deps.length === 0) { console.warn(`${name} should not be used with no dependencies. Use ${hookName} instead.`); } if (deps.every(isPrimitive)) { console.warn(`${name} should not be used with dependencies that are all primitive values. Use ${hookName} instead.`); } }; var checkDeps_default = checkDeps; // src/hooks/useDeepMemo.ts var useDeepMemo = (factory, dependencies) => { if (process.env.NODE_ENV !== "production") { checkDeps_default(dependencies, "useDeepMemo"); } return import_react4.default.useMemo(factory, useDeepMemoize_default(dependencies)); }; var useDeepMemo_default = useDeepMemo; // src/hooks/useContextSelector.ts var useContextSelector = (mapStateToProps, params = {}) => { const { isWithSyncExternalStore = true } = params; if (isWithSyncExternalStore && import_react5.useSyncExternalStore) { const { subscribe, getState } = (0, import_react5.useContext)(Context_default); const getSnapshot = () => { if (!mapStateToProps) { return void 0; } const state2 = getState(); return mapStateToProps(state2); }; return (0, import_react5.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot); } const { state = {} } = (0, import_react5.useContext)(Context_default); let filteredState = {}; if (mapStateToProps) { filteredState = mapStateToProps(state); } else { filteredState = state; } return useDeepMemo_default(() => { return filteredState; }, [filteredState]); }; var useContextSelector_default = useContextSelector; // src/hooks/useDispatcher.ts var import_react6 = require("react"); var useDispatcher = (mapDispatchToProps) => { const { dispatch, effects } = (0, import_react6.useContext)(Context_default); let filteredDispatch = {}; if (mapDispatchToProps) { filteredDispatch = mapDispatchToProps(effects, dispatch); } return filteredDispatch; }; var useDispatcher_default = useDispatcher; // src/connect.tsx var import_jsx_runtime2 = require("react/jsx-runtime"); var connect = (mapStateToProps, mapDispatchToProps) => (Component) => { return (props) => { const { dispatch } = (0, import_react7.useContext)(Context_default); const memoState = useContextSelector_default(mapStateToProps, { isWithSyncExternalStore: false }); const dispatcher = useDispatcher_default(mapDispatchToProps); return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( Component, { ...props, ...memoState, ...dispatcher, dispatch } ); }; }; var connect_default = connect; // src/memoize.ts var import_react8 = __toESM(require("react")); // src/utils/isEqual.ts var isArray = Array.isArray; var keyList = Object.keys; var hasProp = Object.prototype.hasOwnProperty; var hasElementType = typeof Element !== "undefined"; function equal2(a, b) { if (a === b) { return true; } if (a && b && typeof a == "object" && typeof b == "object") { const arrA = isArray(a); const arrB = isArray(b); let length; let key; if (arrA && arrB) { length = a.length; if (length !== b.length) { return false; } for (let i = 0; i < length; i++) { if (!equal2(a[i], b[i])) { return false; } } return true; } if (arrA !== arrB) { return false; } const dateA = a instanceof Date; const dateB = b instanceof Date; if (dateA !== dateB) { return false; } if (dateA && dateB) { return a.getTime() === b.getTime(); } const regexpA = a instanceof RegExp; const regexpB = b instanceof RegExp; if (regexpA !== regexpB) { return false; } if (regexpA && regexpB) { return a.toString() === b.toString(); } const keys = keyList(a); length = keys.length; if (length !== keyList(b).length) return false; for (let i = 0; i < length; i++) { if (!hasProp.call(b, keys[i])) { return false; } } if (hasElementType && a instanceof Element) { return false; } for (let i = 0; i < length; i++) { key = keys[i]; if (key === "_owner" && a.$$typeof) { continue; } else { if (!equal2(a[key], b[key])) { return false; } } } return true; } return a !== a && b !== b; } function exportedEqual(a, b) { try { return equal2(a, b); } catch (error) { if (error.message && error.message.match(/stack|recursion/i) || error.number === -2146828260) { console.warn("Warning: isEqual does not handle circular references.", error.name, error.message); return false; } throw error; } } // src/memoize.ts var shallowDiffers = (prev, next) => { for (let attribute in prev) { if (!(attribute in next)) { return true; } } for (let attribute in next) { if (!exportedEqual(prev[attribute], next[attribute])) { return true; } } return false; }; var areEqual = (prevProps, nextProps) => { const { style: prevStyle, ...prevRest } = prevProps; const { style: nextStyle, ...nextRest } = nextProps; return !shallowDiffers(prevStyle, nextStyle) && !shallowDiffers(prevRest, nextRest); }; var memoize = (Component, memoPropsAreEqual = areEqual) => { return import_react8.default.memo(Component, memoPropsAreEqual); }; var memoize_default = memoize; // src/hooks/useDispatch.ts var import_react9 = require("react"); function useDispatch() { const { dispatch } = (0, import_react9.useContext)(Context_default); return dispatch; } // src/hooks/useSelector.ts var useSelector = (mapStateToProps) => { return useContextSelector_default(mapStateToProps); }; var useSelector_default = useSelector; // src/hooks/useDeepEffect.ts var import_react10 = __toESM(require("react")); var useDeepEffect = (effect, dependencies) => { if (process.env.NODE_ENV !== "production") { checkDeps_default(dependencies, "useDeepEffect"); } return import_react10.default.useEffect(effect, useDeepMemoize_default(dependencies)); }; var useDeepEffect_default = useDeepEffect; // src/hooks/useDeepIsomorphicLayoutEffect.ts var import_react11 = require("react"); var useIsomorphicLayoutEffect = typeof window !== "undefined" ? import_react11.useLayoutEffect : import_react11.useEffect; var useDeepIsomorphicLayoutEffect = (effect, dependencies) => { if (process.env.NODE_ENV !== "production") { checkDeps_default(dependencies, "useDeepEffect"); } return useIsomorphicLayoutEffect(effect, useDeepMemoize_default(dependencies)); }; var useDeepIsomorphicLayoutEffect_default = useDeepIsomorphicLayoutEffect; // src/hooks/useDeepCallback.ts var import_react12 = __toESM(require("react")); var useDeepCallback = (callback, dependencies) => { if (process.env.NODE_ENV !== "production") { checkDeps_default(dependencies, "useDeepCallback"); } return import_react12.default.useCallback(callback, useDeepMemoize_default(dependencies)); }; var useDeepCallback_default = useDeepCallback; // src/index.ts var init = (config) => { return createStore(config); }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Provider, connect, deepEqual, init, isEqual, memoize, useDeepCallback, useDeepEffect, useDeepIsomorphicLayoutEffect, useDeepMemo, useDispatch, useDispatcher, useSelector });