zustand
Version:
🐻 Bear necessities for state management in React
361 lines (355 loc) • 14.1 kB
JavaScript
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 };