|
|
"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);
|
|
|
};
|
|
|
|
|
|
var __read = this && this.__read || function (o, n) {
|
|
|
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
|
if (!m) return o;
|
|
|
var i = m.call(o),
|
|
|
r,
|
|
|
ar = [],
|
|
|
e;
|
|
|
|
|
|
try {
|
|
|
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) {
|
|
|
ar.push(r.value);
|
|
|
}
|
|
|
} catch (error) {
|
|
|
e = {
|
|
|
error: error
|
|
|
};
|
|
|
} finally {
|
|
|
try {
|
|
|
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
|
} finally {
|
|
|
if (e) throw e.error;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return ar;
|
|
|
};
|
|
|
|
|
|
var __spread = this && this.__spread || function () {
|
|
|
for (var ar = [], i = 0; i < arguments.length; i++) {
|
|
|
ar = ar.concat(__read(arguments[i]));
|
|
|
}
|
|
|
|
|
|
return ar;
|
|
|
};
|
|
|
|
|
|
var __importDefault = this && this.__importDefault || function (mod) {
|
|
|
return mod && mod.__esModule ? mod : {
|
|
|
"default": mod
|
|
|
};
|
|
|
};
|
|
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
|
value: true
|
|
|
});
|
|
|
|
|
|
var lodash_debounce_1 = __importDefault(require("lodash.debounce"));
|
|
|
|
|
|
var lodash_throttle_1 = __importDefault(require("lodash.throttle"));
|
|
|
|
|
|
var react_1 = require("react");
|
|
|
|
|
|
var utils_1 = require("./utils");
|
|
|
|
|
|
var cache_1 = require("./utils/cache");
|
|
|
|
|
|
var limit_1 = __importDefault(require("./utils/limit"));
|
|
|
|
|
|
var usePersistFn_1 = __importDefault(require("./utils/usePersistFn"));
|
|
|
|
|
|
var useUpdateEffect_1 = __importDefault(require("./utils/useUpdateEffect"));
|
|
|
|
|
|
var windowFocus_1 = __importDefault(require("./utils/windowFocus"));
|
|
|
|
|
|
var windowVisible_1 = __importDefault(require("./utils/windowVisible"));
|
|
|
|
|
|
var DEFAULT_KEY = 'AHOOKS_USE_REQUEST_DEFAULT_KEY';
|
|
|
|
|
|
var Fetch =
|
|
|
/** @class */
|
|
|
function () {
|
|
|
function Fetch(service, config, subscribe, initState) {
|
|
|
// 请求时序
|
|
|
this.count = 0; // 是否卸载
|
|
|
|
|
|
this.unmountedFlag = false; // visible 后,是否继续轮询
|
|
|
|
|
|
this.pollingWhenVisibleFlag = false;
|
|
|
this.pollingTimer = undefined;
|
|
|
this.loadingDelayTimer = undefined;
|
|
|
this.unsubscribe = [];
|
|
|
this.that = this;
|
|
|
this.state = {
|
|
|
loading: false,
|
|
|
params: [],
|
|
|
data: undefined,
|
|
|
error: undefined,
|
|
|
run: this.run.bind(this.that),
|
|
|
mutate: this.mutate.bind(this.that),
|
|
|
refresh: this.refresh.bind(this.that),
|
|
|
cancel: this.cancel.bind(this.that),
|
|
|
unmount: this.unmount.bind(this.that)
|
|
|
};
|
|
|
this.service = service;
|
|
|
this.config = config;
|
|
|
this.subscribe = subscribe;
|
|
|
|
|
|
if (initState) {
|
|
|
this.state = __assign(__assign({}, this.state), initState);
|
|
|
}
|
|
|
|
|
|
this.debounceRun = this.config.debounceInterval ? lodash_debounce_1["default"](this._run, this.config.debounceInterval) : undefined;
|
|
|
this.throttleRun = this.config.throttleInterval ? lodash_throttle_1["default"](this._run, this.config.throttleInterval) : undefined;
|
|
|
this.limitRefresh = limit_1["default"](this.refresh.bind(this), this.config.focusTimespan);
|
|
|
|
|
|
if (this.config.pollingInterval) {
|
|
|
this.unsubscribe.push(windowVisible_1["default"](this.rePolling.bind(this)));
|
|
|
}
|
|
|
|
|
|
if (this.config.refreshOnWindowFocus) {
|
|
|
this.unsubscribe.push(windowFocus_1["default"](this.limitRefresh.bind(this)));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
Fetch.prototype.setState = function (s) {
|
|
|
if (s === void 0) {
|
|
|
s = {};
|
|
|
}
|
|
|
|
|
|
this.state = __assign(__assign({}, this.state), s);
|
|
|
this.subscribe(this.state);
|
|
|
};
|
|
|
|
|
|
Fetch.prototype._run = function () {
|
|
|
var _this = this;
|
|
|
|
|
|
var args = [];
|
|
|
|
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
|
args[_i] = arguments[_i];
|
|
|
} // 取消已有定时器
|
|
|
|
|
|
|
|
|
if (this.pollingTimer) {
|
|
|
clearTimeout(this.pollingTimer);
|
|
|
} // 取消 loadingDelayTimer
|
|
|
|
|
|
|
|
|
if (this.loadingDelayTimer) {
|
|
|
clearTimeout(this.loadingDelayTimer);
|
|
|
}
|
|
|
|
|
|
this.count += 1; // 闭包存储当次请求的 count
|
|
|
|
|
|
var currentCount = this.count;
|
|
|
this.setState({
|
|
|
loading: !this.config.loadingDelay,
|
|
|
params: args
|
|
|
});
|
|
|
|
|
|
if (this.config.loadingDelay) {
|
|
|
this.loadingDelayTimer = setTimeout(function () {
|
|
|
_this.setState({
|
|
|
loading: true
|
|
|
});
|
|
|
}, this.config.loadingDelay);
|
|
|
}
|
|
|
|
|
|
return this.service.apply(this, __spread(args)).then(function (res) {
|
|
|
if (!_this.unmountedFlag && currentCount === _this.count) {
|
|
|
if (_this.loadingDelayTimer) {
|
|
|
clearTimeout(_this.loadingDelayTimer);
|
|
|
}
|
|
|
|
|
|
var formattedResult = _this.config.formatResult ? _this.config.formatResult(res) : res;
|
|
|
|
|
|
_this.setState({
|
|
|
data: formattedResult,
|
|
|
error: undefined,
|
|
|
loading: false
|
|
|
});
|
|
|
|
|
|
if (_this.config.onSuccess) {
|
|
|
_this.config.onSuccess(formattedResult, args);
|
|
|
}
|
|
|
|
|
|
return formattedResult;
|
|
|
}
|
|
|
})["catch"](function (error) {
|
|
|
if (!_this.unmountedFlag && currentCount === _this.count) {
|
|
|
if (_this.loadingDelayTimer) {
|
|
|
clearTimeout(_this.loadingDelayTimer);
|
|
|
}
|
|
|
|
|
|
_this.setState({
|
|
|
data: undefined,
|
|
|
error: error,
|
|
|
loading: false
|
|
|
});
|
|
|
|
|
|
if (_this.config.onError) {
|
|
|
_this.config.onError(error, args);
|
|
|
} // If throwOnError, user should catch the error self,
|
|
|
// or the page will crash
|
|
|
|
|
|
|
|
|
if (_this.config.throwOnError) {
|
|
|
throw error;
|
|
|
}
|
|
|
|
|
|
console.error(error); // eslint-disable-next-line prefer-promise-reject-errors
|
|
|
|
|
|
return Promise.reject('useRequest has caught the exception, if you need to handle the exception yourself, you can set options.throwOnError to true.');
|
|
|
}
|
|
|
})["finally"](function () {
|
|
|
if (!_this.unmountedFlag && currentCount === _this.count) {
|
|
|
if (_this.config.pollingInterval) {
|
|
|
// 如果屏幕隐藏,并且 !pollingWhenHidden, 则停止轮询,并记录 flag,等 visible 时,继续轮询
|
|
|
if (!utils_1.isDocumentVisible() && !_this.config.pollingWhenHidden) {
|
|
|
_this.pollingWhenVisibleFlag = true;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
_this.pollingTimer = setTimeout(function () {
|
|
|
_this._run.apply(_this, __spread(args));
|
|
|
}, _this.config.pollingInterval);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
};
|
|
|
|
|
|
Fetch.prototype.run = function () {
|
|
|
var args = [];
|
|
|
|
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
|
args[_i] = arguments[_i];
|
|
|
}
|
|
|
|
|
|
if (this.debounceRun) {
|
|
|
this.debounceRun.apply(this, __spread(args)); // TODO 如果 options 存在 debounceInterval,或 throttleInterval,则 run 和 refresh 不会返回 Promise。 带类型需要修复后,此处变成 return;。
|
|
|
|
|
|
return Promise.resolve(null);
|
|
|
}
|
|
|
|
|
|
if (this.throttleRun) {
|
|
|
this.throttleRun.apply(this, __spread(args));
|
|
|
return Promise.resolve(null);
|
|
|
}
|
|
|
|
|
|
return this._run.apply(this, __spread(args));
|
|
|
};
|
|
|
|
|
|
Fetch.prototype.cancel = function () {
|
|
|
if (this.debounceRun) {
|
|
|
this.debounceRun.cancel();
|
|
|
}
|
|
|
|
|
|
if (this.throttleRun) {
|
|
|
this.throttleRun.cancel();
|
|
|
}
|
|
|
|
|
|
if (this.loadingDelayTimer) {
|
|
|
clearTimeout(this.loadingDelayTimer);
|
|
|
}
|
|
|
|
|
|
if (this.pollingTimer) {
|
|
|
clearTimeout(this.pollingTimer);
|
|
|
}
|
|
|
|
|
|
this.pollingWhenVisibleFlag = false;
|
|
|
this.count += 1;
|
|
|
this.setState({
|
|
|
loading: false
|
|
|
});
|
|
|
};
|
|
|
|
|
|
Fetch.prototype.refresh = function () {
|
|
|
return this.run.apply(this, __spread(this.state.params));
|
|
|
};
|
|
|
|
|
|
Fetch.prototype.rePolling = function () {
|
|
|
if (this.pollingWhenVisibleFlag) {
|
|
|
this.pollingWhenVisibleFlag = false;
|
|
|
this.refresh();
|
|
|
}
|
|
|
};
|
|
|
|
|
|
Fetch.prototype.mutate = function (data) {
|
|
|
if (typeof data === 'function') {
|
|
|
this.setState({
|
|
|
// eslint-disable-next-line react/no-access-state-in-setstate
|
|
|
data: data(this.state.data) || {}
|
|
|
});
|
|
|
} else {
|
|
|
this.setState({
|
|
|
data: data
|
|
|
});
|
|
|
}
|
|
|
};
|
|
|
|
|
|
Fetch.prototype.unmount = function () {
|
|
|
this.unmountedFlag = true;
|
|
|
this.cancel();
|
|
|
this.unsubscribe.forEach(function (s) {
|
|
|
s();
|
|
|
});
|
|
|
};
|
|
|
|
|
|
return Fetch;
|
|
|
}();
|
|
|
|
|
|
function useAsync(service, options) {
|
|
|
var _options = options || {};
|
|
|
|
|
|
var _a = _options.refreshDeps,
|
|
|
refreshDeps = _a === void 0 ? [] : _a,
|
|
|
_b = _options.manual,
|
|
|
manual = _b === void 0 ? false : _b,
|
|
|
_c = _options.onSuccess,
|
|
|
onSuccess = _c === void 0 ? function () {} : _c,
|
|
|
_d = _options.onError,
|
|
|
onError = _d === void 0 ? function () {} : _d,
|
|
|
_e = _options.defaultLoading,
|
|
|
defaultLoading = _e === void 0 ? false : _e,
|
|
|
loadingDelay = _options.loadingDelay,
|
|
|
_f = _options.pollingInterval,
|
|
|
pollingInterval = _f === void 0 ? 0 : _f,
|
|
|
_g = _options.pollingWhenHidden,
|
|
|
pollingWhenHidden = _g === void 0 ? true : _g,
|
|
|
_h = _options.defaultParams,
|
|
|
defaultParams = _h === void 0 ? [] : _h,
|
|
|
_j = _options.refreshOnWindowFocus,
|
|
|
refreshOnWindowFocus = _j === void 0 ? false : _j,
|
|
|
_k = _options.focusTimespan,
|
|
|
focusTimespan = _k === void 0 ? 5000 : _k,
|
|
|
fetchKey = _options.fetchKey,
|
|
|
cacheKey = _options.cacheKey,
|
|
|
_l = _options.cacheTime,
|
|
|
cacheTime = _l === void 0 ? 5 * 60 * 1000 : _l,
|
|
|
_m = _options.staleTime,
|
|
|
staleTime = _m === void 0 ? 0 : _m,
|
|
|
debounceInterval = _options.debounceInterval,
|
|
|
throttleInterval = _options.throttleInterval,
|
|
|
initialData = _options.initialData,
|
|
|
_o = _options.ready,
|
|
|
ready = _o === void 0 ? true : _o,
|
|
|
_p = _options.throwOnError,
|
|
|
throwOnError = _p === void 0 ? false : _p;
|
|
|
var newstFetchKey = react_1.useRef(DEFAULT_KEY); // 持久化一些函数
|
|
|
|
|
|
var servicePersist = usePersistFn_1["default"](service);
|
|
|
var onSuccessPersist = usePersistFn_1["default"](onSuccess);
|
|
|
var onErrorPersist = usePersistFn_1["default"](onError);
|
|
|
var fetchKeyPersist = usePersistFn_1["default"](fetchKey);
|
|
|
var formatResult;
|
|
|
|
|
|
if ('formatResult' in _options) {
|
|
|
// eslint-disable-next-line prefer-destructuring
|
|
|
formatResult = _options.formatResult;
|
|
|
}
|
|
|
|
|
|
var formatResultPersist = usePersistFn_1["default"](formatResult);
|
|
|
var config = {
|
|
|
formatResult: formatResultPersist,
|
|
|
onSuccess: onSuccessPersist,
|
|
|
onError: onErrorPersist,
|
|
|
loadingDelay: loadingDelay,
|
|
|
pollingInterval: pollingInterval,
|
|
|
pollingWhenHidden: pollingWhenHidden,
|
|
|
refreshOnWindowFocus: refreshOnWindowFocus,
|
|
|
focusTimespan: focusTimespan,
|
|
|
debounceInterval: debounceInterval,
|
|
|
throttleInterval: throttleInterval,
|
|
|
throwOnError: throwOnError
|
|
|
};
|
|
|
var subscribe = usePersistFn_1["default"](function (key, data) {
|
|
|
setFetches(function (s) {
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
s[key] = data;
|
|
|
return __assign({}, s);
|
|
|
});
|
|
|
});
|
|
|
|
|
|
var _q = __read(react_1.useState(function () {
|
|
|
var _a; // 如果有 缓存,则从缓存中读数据
|
|
|
|
|
|
|
|
|
if (cacheKey) {
|
|
|
var cacheData_1 = (_a = cache_1.getCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.data;
|
|
|
|
|
|
if (cacheData_1) {
|
|
|
newstFetchKey.current = cacheData_1.newstFetchKey;
|
|
|
/* 使用 initState, 重新 new Fetch */
|
|
|
|
|
|
var newFetches_1 = {};
|
|
|
Object.keys(cacheData_1.fetches).forEach(function (key) {
|
|
|
var cacheFetch = cacheData_1.fetches[key];
|
|
|
var newFetch = new Fetch(servicePersist, config, subscribe.bind(null, key), {
|
|
|
loading: cacheFetch.loading,
|
|
|
params: cacheFetch.params,
|
|
|
data: cacheFetch.data,
|
|
|
error: cacheFetch.error
|
|
|
});
|
|
|
newFetches_1[key] = newFetch.state;
|
|
|
});
|
|
|
return newFetches_1;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return [];
|
|
|
}), 2),
|
|
|
fetches = _q[0],
|
|
|
setFetches = _q[1];
|
|
|
|
|
|
var fetchesRef = react_1.useRef(fetches);
|
|
|
fetchesRef.current = fetches;
|
|
|
var readyMemoryParams = react_1.useRef();
|
|
|
var run = react_1.useCallback(function () {
|
|
|
var args = [];
|
|
|
|
|
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
|
args[_i] = arguments[_i];
|
|
|
}
|
|
|
|
|
|
if (!ready) {
|
|
|
// 没有 ready, 记录请求参数,等 ready 后,发起请求用
|
|
|
readyMemoryParams.current = args;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (fetchKeyPersist) {
|
|
|
var key = fetchKeyPersist.apply(void 0, __spread(args));
|
|
|
newstFetchKey.current = key === undefined ? DEFAULT_KEY : key;
|
|
|
}
|
|
|
|
|
|
var currentFetchKey = newstFetchKey.current; // 这里必须用 fetchsRef,而不能用 fetches。
|
|
|
// 否则在 reset 完,立即 run 的时候,这里拿到的 fetches 是旧的。
|
|
|
|
|
|
var currentFetch = fetchesRef.current[currentFetchKey];
|
|
|
|
|
|
if (!currentFetch) {
|
|
|
var newFetch = new Fetch(servicePersist, config, subscribe.bind(null, currentFetchKey), {
|
|
|
data: initialData
|
|
|
});
|
|
|
currentFetch = newFetch.state;
|
|
|
setFetches(function (s) {
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
s[currentFetchKey] = currentFetch;
|
|
|
return __assign({}, s);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
return currentFetch.run.apply(currentFetch, __spread(args));
|
|
|
}, [fetchKey, subscribe, ready]);
|
|
|
var runRef = react_1.useRef(run);
|
|
|
runRef.current = run; // cache
|
|
|
|
|
|
useUpdateEffect_1["default"](function () {
|
|
|
if (cacheKey) {
|
|
|
cache_1.setCache(cacheKey, cacheTime, {
|
|
|
fetches: fetches,
|
|
|
newstFetchKey: newstFetchKey.current
|
|
|
});
|
|
|
}
|
|
|
}, [cacheKey, fetches]); // for ready
|
|
|
|
|
|
var hasTriggeredByReady = react_1.useRef(false);
|
|
|
useUpdateEffect_1["default"](function () {
|
|
|
if (ready) {
|
|
|
if (!hasTriggeredByReady.current && readyMemoryParams.current) {
|
|
|
runRef.current.apply(runRef, __spread(readyMemoryParams.current));
|
|
|
}
|
|
|
|
|
|
hasTriggeredByReady.current = true;
|
|
|
}
|
|
|
}, [ready]); // 第一次默认执行
|
|
|
|
|
|
react_1.useEffect(function () {
|
|
|
var _a;
|
|
|
|
|
|
if (!manual) {
|
|
|
// 如果有缓存,则重新请求
|
|
|
if (Object.keys(fetches).length > 0) {
|
|
|
// 如果 staleTime 是 -1,则 cache 永不过期
|
|
|
// 如果 statleTime 超期了,则重新请求
|
|
|
var cacheStartTime = cacheKey && ((_a = cache_1.getCache(cacheKey)) === null || _a === void 0 ? void 0 : _a.startTime) || 0;
|
|
|
|
|
|
if (!(staleTime === -1 || new Date().getTime() - cacheStartTime <= staleTime)) {
|
|
|
/* 重新执行所有的 cache */
|
|
|
Object.values(fetches).forEach(function (f) {
|
|
|
f.refresh();
|
|
|
});
|
|
|
}
|
|
|
} else {
|
|
|
// 第一次默认执行,可以通过 defaultParams 设置参数
|
|
|
runRef.current.apply(runRef, __spread(defaultParams));
|
|
|
}
|
|
|
}
|
|
|
}, []); // 重置 fetches
|
|
|
|
|
|
var reset = react_1.useCallback(function () {
|
|
|
Object.values(fetchesRef.current).forEach(function (f) {
|
|
|
f.unmount();
|
|
|
});
|
|
|
newstFetchKey.current = DEFAULT_KEY;
|
|
|
setFetches({}); // 不写会有问题。如果不写,此时立即 run,会是老的数据
|
|
|
|
|
|
fetchesRef.current = {};
|
|
|
}, [setFetches]); // refreshDeps 变化,重新执行所有请求
|
|
|
|
|
|
useUpdateEffect_1["default"](function () {
|
|
|
if (!manual) {
|
|
|
/* 全部重新执行 */
|
|
|
Object.values(fetchesRef.current).forEach(function (f) {
|
|
|
f.refresh();
|
|
|
});
|
|
|
}
|
|
|
}, __spread(refreshDeps)); // 卸载组件触发
|
|
|
|
|
|
react_1.useEffect(function () {
|
|
|
return function () {
|
|
|
Object.values(fetchesRef.current).forEach(function (f) {
|
|
|
f.unmount();
|
|
|
});
|
|
|
};
|
|
|
}, []);
|
|
|
var notExecutedWarning = react_1.useCallback(function (name) {
|
|
|
return function () {
|
|
|
console.warn("You should't call " + name + " when service not executed once.");
|
|
|
};
|
|
|
}, []);
|
|
|
return __assign(__assign({
|
|
|
loading: ready && !manual || defaultLoading,
|
|
|
data: initialData,
|
|
|
error: undefined,
|
|
|
params: [],
|
|
|
cancel: notExecutedWarning('cancel'),
|
|
|
refresh: notExecutedWarning('refresh'),
|
|
|
mutate: notExecutedWarning('mutate')
|
|
|
}, fetches[newstFetchKey.current] || {}), {
|
|
|
run: run,
|
|
|
fetches: fetches,
|
|
|
reset: reset
|
|
|
});
|
|
|
}
|
|
|
|
|
|
exports["default"] = useAsync; |