UNPKG

vue-use-query

Version:

vue use query

339 lines (338 loc) 14.8 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Query = void 0; var utils_1 = require("./utils"); var notifyManager_1 = require("./notifyManager"); var logger_1 = require("./logger"); var retryer_1 = require("./retryer"); // CLASS var Query = /** @class */ (function () { function Query(config) { this.defaultOptions = config.defaultOptions; this.setOptions(config.options); this.observers = []; this.cache = config.cache; this.queryKey = config.queryKey; this.queryHash = config.queryHash; this.initialState = config.state || this.getDefaultState(this.options); this.state = this.initialState; this.scheduleGc(); } Query.prototype.setOptions = function (options) { var _a; this.options = __assign(__assign({}, this.defaultOptions), options); // Default to 5 minutes if not cache time is set this.cacheTime = Math.max(this.cacheTime || 0, (_a = this.options.cacheTime) !== null && _a !== void 0 ? _a : 5 * 60 * 1000); }; Query.prototype.setDefaultOptions = function (options) { this.defaultOptions = options; }; Query.prototype.scheduleGc = function () { var _this = this; this.clearGcTimeout(); if (utils_1.isValidTimeout(this.cacheTime)) { this.gcTimeout = setTimeout(function () { _this.optionalRemove(); }, this.cacheTime); } }; Query.prototype.clearGcTimeout = function () { clearTimeout(this.gcTimeout); this.gcTimeout = undefined; }; Query.prototype.optionalRemove = function () { if (!this.observers.length && !this.state.isFetching) { this.cache.remove(this); } }; Query.prototype.setData = function (updater, options) { var _a, _b; var prevData = this.state.data; // Get the new data var data = utils_1.functionalUpdate(updater, prevData); // Use prev data if an isDataEqual function is defined and returns `true` if ((_b = (_a = this.options).isDataEqual) === null || _b === void 0 ? void 0 : _b.call(_a, prevData, data)) { data = prevData; } else if (this.options.structuralSharing !== false) { // Structurally share data between prev and new data if needed data = utils_1.replaceEqualDeep(prevData, data); } // Set data and mark it as cached this.dispatch({ data: data, type: 'success', dataUpdatedAt: options === null || options === void 0 ? void 0 : options.updatedAt, }); return data; }; Query.prototype.setState = function (state, setStateOptions) { this.dispatch({ type: 'setState', state: state, setStateOptions: setStateOptions }); }; Query.prototype.cancel = function (options) { var _a; var promise = this.promise; (_a = this.retryer) === null || _a === void 0 ? void 0 : _a.cancel(options); return promise ? promise.then(utils_1.noop).catch(utils_1.noop) : Promise.resolve(); }; Query.prototype.destroy = function () { this.clearGcTimeout(); this.cancel({ silent: true }); }; Query.prototype.reset = function () { this.destroy(); this.setState(this.initialState); }; Query.prototype.isActive = function () { return this.observers.some(function (observer) { return observer.options.enabled !== false; }); }; Query.prototype.isFetching = function () { return this.state.isFetching; }; Query.prototype.isStale = function () { return (this.state.isInvalidated || !this.state.dataUpdatedAt || this.observers.some(function (observer) { return observer.getCurrentResult().isStale; })); }; Query.prototype.isStaleByTime = function (staleTime) { if (staleTime === void 0) { staleTime = 0; } return (this.state.isInvalidated || !this.state.dataUpdatedAt || !utils_1.timeUntilStale(this.state.dataUpdatedAt, staleTime)); }; Query.prototype.onFocus = function () { var _a; var observer = this.observers.find(function (x) { return x.shouldFetchOnWindowFocus(); }); if (observer) { observer.refetch(); } // Continue fetch if currently paused (_a = this.retryer) === null || _a === void 0 ? void 0 : _a.continue(); }; Query.prototype.onOnline = function () { var _a; var observer = this.observers.find(function (x) { return x.shouldFetchOnReconnect(); }); if (observer) { observer.refetch(); } // Continue fetch if currently paused (_a = this.retryer) === null || _a === void 0 ? void 0 : _a.continue(); }; Query.prototype.addObserver = function (observer) { if (this.observers.indexOf(observer) === -1) { this.observers.push(observer); // Stop the query from being garbage collected this.clearGcTimeout(); this.cache.notify({ type: 'observerAdded', query: this, observer: observer }); } }; Query.prototype.removeObserver = function (observer) { if (this.observers.indexOf(observer) !== -1) { this.observers = this.observers.filter(function (x) { return x !== observer; }); if (!this.observers.length) { // If the transport layer does not support cancellation // we'll let the query continue so the result can be cached if (this.retryer) { if (this.retryer.isTransportCancelable) { this.retryer.cancel({ revert: true }); } else { this.retryer.cancelRetry(); } } if (this.cacheTime) { this.scheduleGc(); } else { this.cache.remove(this); } } this.cache.notify({ type: 'observerRemoved', query: this, observer: observer }); } }; Query.prototype.getObserversCount = function () { return this.observers.length; }; Query.prototype.invalidate = function () { if (!this.state.isInvalidated) { this.dispatch({ type: 'invalidate' }); } }; Query.prototype.fetch = function (options, fetchOptions) { var _this = this; var _a, _b, _c, _d; if (this.state.isFetching) { if (this.state.dataUpdatedAt && (fetchOptions === null || fetchOptions === void 0 ? void 0 : fetchOptions.cancelRefetch)) { // Silently cancel current fetch if the user wants to cancel refetches this.cancel({ silent: true }); } else if (this.promise) { // Return current promise if we are already fetching return this.promise; } } // Update config if passed, otherwise the config from the last execution is used if (options) { this.setOptions(options); } // Use the options from the first observer with a query function if no function is found. // This can happen when the query is hydrated or created with setQueryData. if (!this.options.queryFn) { var observer = this.observers.find(function (x) { return x.options.queryFn; }); if (observer) { this.setOptions(observer.options); } } var queryKey = utils_1.ensureQueryKeyArray(this.queryKey); // Create query function context var queryFnContext = { queryKey: queryKey, pageParam: undefined, }; // Create fetch function var fetchFn = function () { return _this.options.queryFn ? _this.options.queryFn(queryFnContext) : Promise.reject('Missing queryFn'); }; // Trigger behavior hook var context = { fetchOptions: fetchOptions, options: this.options, queryKey: queryKey, state: this.state, fetchFn: fetchFn, }; if ((_a = this.options.behavior) === null || _a === void 0 ? void 0 : _a.onFetch) { (_b = this.options.behavior) === null || _b === void 0 ? void 0 : _b.onFetch(context); } // Store state in case the current fetch needs to be reverted this.revertState = this.state; // Set to fetching state if not already in it if (!this.state.isFetching || this.state.fetchMeta !== ((_c = context.fetchOptions) === null || _c === void 0 ? void 0 : _c.meta)) { this.dispatch({ type: 'fetch', meta: (_d = context.fetchOptions) === null || _d === void 0 ? void 0 : _d.meta }); } // Try to fetch the data this.retryer = new retryer_1.Retryer({ fn: context.fetchFn, onSuccess: function (data) { _this.setData(data); // Remove query after fetching if cache time is 0 if (_this.cacheTime === 0) { _this.optionalRemove(); } }, onError: function (error) { // Optimistically update state if needed if (!(retryer_1.isCancelledError(error) && error.silent)) { _this.dispatch({ type: 'error', error: error, }); } if (!retryer_1.isCancelledError(error)) { // Notify cache callback if (_this.cache.config.onError) { _this.cache.config.onError(error, _this); } // Log error logger_1.getLogger().error(error); } // Remove query after fetching if cache time is 0 if (_this.cacheTime === 0) { _this.optionalRemove(); } }, onFail: function () { _this.dispatch({ type: 'failed' }); }, onPause: function () { _this.dispatch({ type: 'pause' }); }, onContinue: function () { _this.dispatch({ type: 'continue' }); }, retry: context.options.retry, retryDelay: context.options.retryDelay, }); this.promise = this.retryer.promise; return this.promise; }; Query.prototype.dispatch = function (action) { var _this = this; this.state = this.reducer(this.state, action); notifyManager_1.notifyManager.batch(function () { _this.observers.forEach(function (observer) { observer.onQueryUpdate(action); }); _this.cache.notify({ query: _this, type: 'queryUpdated', action: action }); }); }; Query.prototype.getDefaultState = function (options) { var data = typeof options.initialData === 'function' ? options.initialData() : options.initialData; var hasInitialData = typeof options.initialData !== 'undefined'; var initialDataUpdatedAt = hasInitialData ? typeof options.initialDataUpdatedAt === 'function' ? options.initialDataUpdatedAt() : options.initialDataUpdatedAt : 0; var hasData = typeof data !== 'undefined'; return { data: data, dataUpdateCount: 0, dataUpdatedAt: hasData ? initialDataUpdatedAt !== null && initialDataUpdatedAt !== void 0 ? initialDataUpdatedAt : Date.now() : 0, error: null, errorUpdateCount: 0, errorUpdatedAt: 0, fetchFailureCount: 0, fetchMeta: null, isFetching: false, isInvalidated: false, isPaused: false, status: hasData ? 'success' : 'idle', }; }; Query.prototype.reducer = function (state, action) { var _a, _b; switch (action.type) { case 'failed': return __assign(__assign({}, state), { fetchFailureCount: state.fetchFailureCount + 1 }); case 'pause': return __assign(__assign({}, state), { isPaused: true }); case 'continue': return __assign(__assign({}, state), { isPaused: false }); case 'fetch': return __assign(__assign({}, state), { fetchFailureCount: 0, fetchMeta: (_a = action.meta) !== null && _a !== void 0 ? _a : null, isFetching: true, isPaused: false, status: !state.dataUpdatedAt ? 'loading' : state.status }); case 'success': return __assign(__assign({}, state), { data: action.data, dataUpdateCount: state.dataUpdateCount + 1, dataUpdatedAt: (_b = action.dataUpdatedAt) !== null && _b !== void 0 ? _b : Date.now(), error: null, fetchFailureCount: 0, isFetching: false, isInvalidated: false, isPaused: false, status: 'success' }); case 'error': var error = action.error; if (retryer_1.isCancelledError(error) && error.revert && this.revertState) { return __assign({}, this.revertState); } return __assign(__assign({}, state), { error: error, errorUpdateCount: state.errorUpdateCount + 1, errorUpdatedAt: Date.now(), fetchFailureCount: state.fetchFailureCount + 1, isFetching: false, isPaused: false, status: 'error' }); case 'invalidate': return __assign(__assign({}, state), { isInvalidated: true }); case 'setState': return __assign(__assign({}, state), action.state); default: return state; } }; return Query; }()); exports.Query = Query;