rc-hooks
Version:
React Hooks Library.
209 lines (208 loc) • 11.1 kB
JavaScript
import { __assign, __read } from "tslib";
import { useState, useEffect, useCallback, useRef } from 'react';
import { equalArrayLike, isArray, isUndefined, noop } from 'ut2';
import usePersistFn from '../usePersistFn';
import useUpdateEffect from '../useUpdateEffect';
import useLatest from '../useLatest';
import useUnmountedRef from '../useUnmountedRef';
import AsyncCalss, { getCache, clearCache } from './Async';
export { clearCache };
/**
* 管理异步函数。
*
* @param {Function} asyncFn 异步函数。
* @param {Object} [options] 配置项。
* @param {boolean} [options.autoRun=true] 在初始化时自动执行异步函数。如果设置为 `false`,则需要手动调用 `run` 触发执行。默认 `true`。
* @param {*} [options.initialData] 初始化的 `data`。
* @param {boolean} [options.defaultLoading=false] 初始化默认 `loading` 值。默认值等于 `autoRun && !loadingDelay`。
* @param {Array} [options.defaultParams] 如果 `autoRun=true` 自动执行 `run` 的默认参数。
* @param {Array} [options.refreshDeps] 在 `autoRun = true` 时,`refreshDeps` 变化,会触发重新执行。
* @param {Function} [options.onBefore] 异步函数执行前触发,参数为 `params`。
* @param {Function} [options.onSuccess] 异步函数 `resolve` 时触发,参数为 `data` 和 `params`。
* @param {Function} [options.onError] 异步函数报错时触发,参数为 `error` 和 `params`。
* @param {string} [options.cacheKey] 缓存的键值。启用缓存机制,异步成功结果将被缓存。如果多个相同 cacheKey 的异步同时触发中,将共享第一个异步结果。
* @param {number} [options.cacheTime=5*60*1000] 缓存时间。单位毫秒。
* @param {boolean} [options.persisted=false] 持久化数据。当有缓存数据时,不再执行异步函数。需要配合 `cacheKey` `cacheTime` 使用。默认 `false`。
* @param {number} [options.loadingDelay] 设置 `loading` 延迟时间,避免闪烁,单位为毫秒。
* @param {number} [options.pollingInterval] 轮询间隔,单位为毫秒。设置后,将进入轮询模式,定时触发 `run`。
* @param {boolean} [options.pollingWhenHidden=true] 在页面隐藏时,是否继续轮询。如果为 `true`,不会停止轮询。如果为 `false`,在页面隐藏时会暂时停止轮询,页面重新显示时继续上次轮询。默认 `true`。
* @param {boolean} [options.refreshOnWindowFocus=false] 在屏幕重新获取焦点或重新显示时,是否重新发起请求。如果为 `false`,不会重新发起请求。如果为 `true`,在屏幕重新聚焦或重新显示时,会重新发起请求。默认 `false`。
* @param {number} [options.focusTimespan=5000] 屏幕重新聚焦,重新发起请求时间间隔。需要配置 `refreshOnWindowFocus` 使用。默认 `5000`。
* @param {number} [options.debounceInterval] 防抖间隔,单位为毫秒,设置后,请求进入防抖模式。
* @param {number} [options.throttleInterval] 节流间隔,单位为毫秒,设置后,请求进入节流模式。
* @returns {Object}
* @example
* const { data, error, loading, run, cancel, refresh, mutate } = useAsync(asyncFn, options);
*/
var useAsync = function (asyncFn, options) {
var _a = options || {}, _b = _a.autoRun, autoRun = _b === void 0 ? true : _b, _c = _a.refreshDeps, refreshDeps = _c === void 0 ? [] : _c, defaultParams = _a.defaultParams, loadingDelay = _a.loadingDelay, __INTERNAL_FORMAT__ = _a.__INTERNAL_FORMAT__, defaultLoading = _a.defaultLoading, initialData = _a.initialData, _d = _a.cacheKey, cacheKey = _d === void 0 ? '' : _d, _e = _a.cacheTime, cacheTime = _e === void 0 ? 5 * 60 * 1000 : _e, _f = _a.persisted, persisted = _f === void 0 ? false : _f, _g = _a.onSuccess, onSuccess = _g === void 0 ? noop : _g, _h = _a.onError, onError = _h === void 0 ? noop : _h, _j = _a.onFinally, onFinally = _j === void 0 ? noop : _j, _k = _a.onBefore, onBefore = _k === void 0 ? noop : _k, _l = _a.pollingInterval, pollingInterval = _l === void 0 ? 0 : _l, _m = _a.pollingWhenHidden, pollingWhenHidden = _m === void 0 ? true : _m, _o = _a.refreshOnWindowFocus, refreshOnWindowFocus = _o === void 0 ? false : _o, _p = _a.focusTimespan, focusTimespan = _p === void 0 ? 5000 : _p, debounceInterval = _a.debounceInterval, throttleInterval = _a.throttleInterval;
var _q = __read(useState(function () { return ({
// 参数兼容非array的情况
params: [],
loading: !!(isUndefined(defaultLoading) ? autoRun && !loadingDelay : defaultLoading),
error: null,
data: cacheKey ? getCache(cacheKey) : initialData
}); }), 2), state = _q[0], set = _q[1];
var latestState = useLatest(state);
var unmountedRef = useUnmountedRef();
var loadingDelayTimerRef = useRef(null); // 延迟loading
// 持久化一些函数
var asyncFnPersist = usePersistFn(asyncFn);
var onSuccessPersist = usePersistFn(onSuccess);
var onErrorPersist = usePersistFn(onError);
var onFinallyPersist = usePersistFn(onFinally);
var onBeforePersist = usePersistFn(onBefore);
var internalFormatRef = useLatest(__INTERNAL_FORMAT__);
// 异步执行前
var handleBefore = useCallback(function (p) {
onBeforePersist(p);
// 取消延迟loading
if (loadingDelayTimerRef.current) {
clearTimeout(loadingDelayTimerRef.current);
}
// 缓存数据
var cacheData = cacheKey ? getCache(cacheKey) : undefined;
// 没有缓存数据 或 没有开启持久缓存,设置loading
if (!cacheData || !persisted) {
if (latestState.current.loading !== !loadingDelay ||
!equalArrayLike(latestState.current.params, p)) {
set(function (s) { return (__assign(__assign({}, s), { loading: !loadingDelay, params: p })); });
}
// 设置延迟loading定时器
if (loadingDelay) {
loadingDelayTimerRef.current = setTimeout(function () {
if (!unmountedRef.current) {
set(function (s) { return (__assign(__assign({}, s), { loading: true })); });
}
}, loadingDelay);
}
}
}, [cacheKey, latestState, loadingDelay, onBeforePersist, persisted, unmountedRef]);
// 异步执行成功后
var handleSuccess = useCallback(function (res, args) {
if (loadingDelayTimerRef.current) {
clearTimeout(loadingDelayTimerRef.current);
}
set(function (s) { return (__assign(__assign({}, s), { data: res, error: null, loading: false })); });
onSuccessPersist(res, args);
}, [onSuccessPersist]);
// 异步执行失败后
var handleError = useCallback(function (err, args) {
if (loadingDelayTimerRef.current) {
clearTimeout(loadingDelayTimerRef.current);
}
set(function (s) { return (__assign(__assign({}, s), { error: err, loading: false })); });
onErrorPersist(err, args);
}, [onErrorPersist]);
// @ts-ignore
var asyncInstanceRef = useRef();
if (!asyncInstanceRef.current) {
asyncInstanceRef.current = new AsyncCalss(asyncFnPersist, {
cacheKey: cacheKey,
cacheTime: cacheTime,
persisted: persisted,
formatResult: internalFormatRef.current,
onSuccess: handleSuccess,
onError: handleError,
onFinally: onFinallyPersist,
onBefore: handleBefore,
debounceInterval: debounceInterval,
throttleInterval: throttleInterval,
pollingInterval: pollingInterval,
pollingWhenHidden: pollingWhenHidden,
refreshOnWindowFocus: refreshOnWindowFocus,
focusTimespan: focusTimespan
});
}
useUpdateEffect(function () {
asyncInstanceRef.current.updateOptions({
cacheKey: cacheKey,
cacheTime: cacheTime,
persisted: persisted,
formatResult: internalFormatRef.current,
onSuccess: handleSuccess,
onError: handleError,
onFinally: onFinallyPersist,
onBefore: handleBefore,
debounceInterval: debounceInterval,
throttleInterval: throttleInterval,
pollingInterval: pollingInterval,
pollingWhenHidden: pollingWhenHidden,
refreshOnWindowFocus: refreshOnWindowFocus,
focusTimespan: focusTimespan
});
}, [
onSuccessPersist,
onErrorPersist,
cacheKey,
cacheTime,
persisted,
debounceInterval,
throttleInterval,
onFinallyPersist,
onBeforePersist,
pollingInterval,
pollingWhenHidden,
refreshOnWindowFocus,
focusTimespan,
handleSuccess,
handleError,
handleBefore
]);
// 执行异步
var run = useCallback(function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return asyncInstanceRef.current.run.apply(asyncInstanceRef.current, args);
}, []);
// 使用上一次执行异步的参数,重新执行
var refresh = useCallback(function () {
return asyncInstanceRef.current.refresh();
}, []);
var cancel = useCallback(function () {
asyncInstanceRef.current.cancel();
// 取消延迟loading
if (loadingDelayTimerRef.current) {
clearTimeout(loadingDelayTimerRef.current);
}
set(function (s) { return (__assign(__assign({}, s), { loading: false })); });
}, []);
// 突变
var mutate = function (newData) {
if (typeof newData === 'function') {
// @ts-ignore
set(function (s) { return (__assign(__assign({}, s), { data: newData(state.data) })); });
}
else {
set(function (s) { return (__assign(__assign({}, s), { data: newData })); });
}
};
// autoRun=true 时,refreshDeps 变化,将重新执行
useUpdateEffect(function () {
// 区分 React.StrictMode 下触发
if (autoRun && isArray(refreshDeps) && refreshDeps.length > 0) {
refresh();
}
}, [autoRun].concat(refreshDeps));
useEffect(function () {
// 默认自动执行
if (autoRun) {
// 支持默认参数
var fmtDefaultParams = isArray(defaultParams)
? defaultParams
: (typeof defaultParams !== 'undefined' ? [defaultParams] : []);
run.apply(void 0, fmtDefaultParams);
}
// 如果销毁过,可以重新恢复异步实例
asyncInstanceRef.current.resume();
return function () {
cancel();
asyncInstanceRef.current.destroy(false);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return __assign(__assign({}, state), { run: run, cancel: cancel, mutate: mutate, refresh: refresh });
};
export default useAsync;