UNPKG

zustand

Version:

🐻 Bear necessities for state management in React

361 lines (355 loc) 14.1 kB
var __defProp$1 = Object.defineProperty; var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols; var __hasOwnProp$1 = Object.prototype.hasOwnProperty; var __propIsEnum$1 = Object.prototype.propertyIsEnumerable; var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues$1 = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp$1.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); if (__getOwnPropSymbols$1) for (var prop of __getOwnPropSymbols$1(b)) { if (__propIsEnum$1.call(b, prop)) __defNormalProp$1(a, prop, b[prop]); } return a; }; const redux = (reducer, initial) => (set, get, api) => { api.dispatch = (action) => { set((state) => reducer(state, action), false, action); return action; }; api.dispatchFromDevtools = true; return __spreadValues$1({ dispatch: (...a) => api.dispatch(...a) }, initial); }; function devtools(fn, options) { return (set, get, api) => { let didWarnAboutNameDeprecation = false; if (typeof options === "string" && !didWarnAboutNameDeprecation) { console.warn("[zustand devtools middleware]: passing `name` as directly will be not allowed in next majorpass the `name` in an object `{ name: ... }` instead"); didWarnAboutNameDeprecation = true; } const devtoolsOptions = options === void 0 ? { name: void 0, anonymousActionType: void 0 } : typeof options === "string" ? { name: options } : options; let extensionConnector; try { extensionConnector = window.__REDUX_DEVTOOLS_EXTENSION__ || window.top.__REDUX_DEVTOOLS_EXTENSION__; } catch { } if (!extensionConnector) { if (process.env.NODE_ENV === "development" && typeof window !== "undefined") { console.warn("[zustand devtools middleware] Please install/enable Redux devtools extension"); } return fn(set, get, api); } let extension = Object.create(extensionConnector.connect(devtoolsOptions)); let didWarnAboutDevtools = false; Object.defineProperty(api, "devtools", { get: () => { if (!didWarnAboutDevtools) { console.warn("[zustand devtools middleware] `devtools` property on the store is deprecated it will be removed in the next major.\nYou shouldn't interact with the extension directly. But in case you still want to you can patch `window.__REDUX_DEVTOOLS_EXTENSION__` directly"); didWarnAboutDevtools = true; } return extension; }, set: (value) => { if (!didWarnAboutDevtools) { console.warn("[zustand devtools middleware] `api.devtools` is deprecated, it will be removed in the next major.\nYou shouldn't interact with the extension directly. But in case you still want to you can patch `window.__REDUX_DEVTOOLS_EXTENSION__` directly"); didWarnAboutDevtools = true; } extension = value; } }); let didWarnAboutPrefix = false; Object.defineProperty(extension, "prefix", { get: () => { if (!didWarnAboutPrefix) { console.warn("[zustand devtools middleware] along with `api.devtools`, `api.devtools.prefix` is deprecated.\nWe no longer prefix the actions/names" + devtoolsOptions.name === void 0 ? ", pass the `name` option to create a separate instance of devtools for each store." : ", because the `name` option already creates a separate instance of devtools for each store."); didWarnAboutPrefix = true; } return ""; }, set: () => { if (!didWarnAboutPrefix) { console.warn("[zustand devtools middleware] along with `api.devtools`, `api.devtools.prefix` is deprecated.\nWe no longer prefix the actions/names" + devtoolsOptions.name === void 0 ? ", pass the `name` option to create a separate instance of devtools for each store." : ", because the `name` option already creates a separate instance of devtools for each store."); didWarnAboutPrefix = true; } } }); let isRecording = true; api.setState = (state, replace, nameOrAction) => { set(state, replace); if (!isRecording) return; extension.send(nameOrAction === void 0 ? { type: devtoolsOptions.anonymousActionType || "anonymous" } : typeof nameOrAction === "string" ? { type: nameOrAction } : nameOrAction, get()); }; const setStateFromDevtools = (...a) => { const originalIsRecording = isRecording; isRecording = false; set(...a); isRecording = originalIsRecording; }; const initialState = fn(api.setState, get, api); extension.init(initialState); if (api.dispatchFromDevtools && typeof api.dispatch === "function") { let didWarnAboutReservedActionType = false; const originalDispatch = api.dispatch; api.dispatch = (...a) => { if (a[0].type === "__setState" && !didWarnAboutReservedActionType) { console.warn('[zustand devtools middleware] "__setState" action type is reserved to set state from the devtools. Avoid using it.'); didWarnAboutReservedActionType = true; } originalDispatch(...a); }; } extension.subscribe((message) => { var _a; switch (message.type) { case "ACTION": if (typeof message.payload !== "string") { console.error("[zustand devtools middleware] Unsupported action format"); return; } return parseJsonThen(message.payload, (action) => { if (action.type === "__setState") { setStateFromDevtools(action.state); return; } if (!api.dispatchFromDevtools) return; if (typeof api.dispatch !== "function") return; api.dispatch(action); }); case "DISPATCH": switch (message.payload.type) { case "RESET": setStateFromDevtools(initialState); return extension.init(api.getState()); case "COMMIT": return extension.init(api.getState()); case "ROLLBACK": return parseJsonThen(message.state, (state) => { setStateFromDevtools(state); extension.init(api.getState()); }); case "JUMP_TO_STATE": case "JUMP_TO_ACTION": return parseJsonThen(message.state, (state) => { setStateFromDevtools(state); }); case "IMPORT_STATE": { const { nextLiftedState } = message.payload; const lastComputedState = (_a = nextLiftedState.computedStates.slice(-1)[0]) == null ? void 0 : _a.state; if (!lastComputedState) return; setStateFromDevtools(lastComputedState); extension.send(null, nextLiftedState); return; } case "PAUSE_RECORDING": return isRecording = !isRecording; } return; } }); return initialState; }; } const parseJsonThen = (stringified, f) => { let parsed; try { parsed = JSON.parse(stringified); } catch (e) { console.error("[zustand devtools middleware] Could not parse the received json", e); } if (parsed !== void 0) f(parsed); }; const subscribeWithSelector = (fn) => (set, get, api) => { const origSubscribe = api.subscribe; api.subscribe = (selector, optListener, options) => { let listener = selector; if (optListener) { const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is; let currentSlice = selector(api.getState()); listener = (state) => { const nextSlice = selector(state); if (!equalityFn(currentSlice, nextSlice)) { const previousSlice = currentSlice; optListener(currentSlice = nextSlice, previousSlice); } }; if (options == null ? void 0 : options.fireImmediately) { optListener(currentSlice, currentSlice); } } return origSubscribe(listener); }; const initialState = fn(set, get, api); return initialState; }; const combine = (initialState, create) => (set, get, api) => Object.assign({}, initialState, create(set, get, api)); var __defProp = Object.defineProperty; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; const toThenable = (fn) => (input) => { try { const result = fn(input); if (result instanceof Promise) { return result; } return { then(onFulfilled) { return toThenable(onFulfilled)(result); }, catch(_onRejected) { return this; } }; } catch (e) { return { then(_onFulfilled) { return this; }, catch(onRejected) { return toThenable(onRejected)(e); } }; } }; const persist = (config, baseOptions) => (set, get, api) => { let options = __spreadValues({ getStorage: () => localStorage, serialize: JSON.stringify, deserialize: JSON.parse, partialize: (state) => state, version: 0, merge: (persistedState, currentState) => __spreadValues(__spreadValues({}, currentState), persistedState) }, baseOptions); if (options.blacklist || options.whitelist) { console.warn(`The ${options.blacklist ? "blacklist" : "whitelist"} option is deprecated and will be removed in the next version. Please use the 'partialize' option instead.`); } let hasHydrated = false; const hydrationListeners = /* @__PURE__ */ new Set(); const finishHydrationListeners = /* @__PURE__ */ new Set(); let storage; try { storage = options.getStorage(); } catch (e) { } if (!storage) { return config((...args) => { console.warn(`[zustand persist middleware] Unable to update item '${options.name}', the given storage is currently unavailable.`); set(...args); }, get, api); } else if (!storage.removeItem) { console.warn(`[zustand persist middleware] The given storage for item '${options.name}' does not contain a 'removeItem' method, which will be required in v4.`); } const thenableSerialize = toThenable(options.serialize); const setItem = () => { const state = options.partialize(__spreadValues({}, get())); if (options.whitelist) { Object.keys(state).forEach((key) => { var _a; !((_a = options.whitelist) == null ? void 0 : _a.includes(key)) && delete state[key]; }); } if (options.blacklist) { options.blacklist.forEach((key) => delete state[key]); } let errorInSync; const thenable = thenableSerialize({ state, version: options.version }).then((serializedValue) => storage.setItem(options.name, serializedValue)).catch((e) => { errorInSync = e; }); if (errorInSync) { throw errorInSync; } return thenable; }; const savedSetState = api.setState; api.setState = (state, replace) => { savedSetState(state, replace); void setItem(); }; const configResult = config((...args) => { set(...args); void setItem(); }, get, api); let stateFromStorage; const hydrate = () => { var _a; if (!storage) return; hasHydrated = false; hydrationListeners.forEach((cb) => cb(get())); const postRehydrationCallback = ((_a = options.onRehydrateStorage) == null ? void 0 : _a.call(options, get())) || void 0; return toThenable(storage.getItem.bind(storage))(options.name).then((storageValue) => { if (storageValue) { return options.deserialize(storageValue); } }).then((deserializedStorageValue) => { if (deserializedStorageValue) { if (typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== options.version) { if (options.migrate) { return options.migrate(deserializedStorageValue.state, deserializedStorageValue.version); } console.error(`State loaded from storage couldn't be migrated since no migrate function was provided`); } else { return deserializedStorageValue.state; } } }).then((migratedState) => { stateFromStorage = options.merge(migratedState, configResult); set(stateFromStorage, true); return setItem(); }).then(() => { postRehydrationCallback == null ? void 0 : postRehydrationCallback(stateFromStorage, void 0); hasHydrated = true; finishHydrationListeners.forEach((cb) => cb(stateFromStorage)); }).catch((e) => { postRehydrationCallback == null ? void 0 : postRehydrationCallback(void 0, e); }); }; api.persist = { setOptions: (newOptions) => { options = __spreadValues(__spreadValues({}, options), newOptions); if (newOptions.getStorage) { storage = newOptions.getStorage(); } }, clearStorage: () => { var _a; (_a = storage == null ? void 0 : storage.removeItem) == null ? void 0 : _a.call(storage, options.name); }, rehydrate: () => hydrate(), hasHydrated: () => hasHydrated, onHydrate: (cb) => { hydrationListeners.add(cb); return () => { hydrationListeners.delete(cb); }; }, onFinishHydration: (cb) => { finishHydrationListeners.add(cb); return () => { finishHydrationListeners.delete(cb); }; } }; hydrate(); return stateFromStorage || configResult; }; export { combine, devtools, persist, redux, subscribeWithSelector };