react-use-api
Version:
<div align="center"> <h1> <img width="120" src="./icon.svg"> <br/> React useApi() <br /> <br /> </h1> </div>
359 lines • 16.5 kB
JavaScript
;
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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyConfig = exports.handleUseApiOptions = exports.fetchApi = exports.reducer = exports.useApi = void 0;
var react_1 = require("react");
var context_1 = require("./context");
var common_1 = require("./common");
function useApi(config, opt) {
var _this = this;
if (typeof config === 'string') {
config = {
url: config,
};
}
var context = react_1.useContext(context_1.ApiContext);
var settings = context.settings, _a = context.settings, cache = _a.cache, debug = _a.debug, clearLastCacheWhenConfigChanges = _a.clearLastCacheWhenConfigChanges, isSSR = context.isSSR, _b = context.collection, ssrConfigs = _b.ssrConfigs, cacheKeys = _b.cacheKeys;
var cacheKey = JSON.stringify(config);
var ref = react_1.useRef(react_1.useMemo(function () {
return ({
id: Date.now(),
isRequesting: false,
isInit: false,
hasFed: false,
refreshFlag: 1,
cacheKey: cacheKey,
config: config,
});
}, []));
var options = react_1.useMemo(function () { return exports.handleUseApiOptions(opt, settings, config, cacheKey); }, [opt, settings, cacheKey]);
var refData = ref.current;
var isValidConfig = exports.verifyConfig(config);
var hasChangedConfig = refData.cacheKey !== cacheKey;
options.$hasChangedConfig = hasChangedConfig;
if (hasChangedConfig) {
if (clearLastCacheWhenConfigChanges) {
cache.del(refData.cacheKey);
}
refData.cacheKey = cacheKey;
refData.config = config;
refData.hasFed = false;
}
// SSR processing
var cacheData = cache.get(cacheKey);
var skip = options.skip, useCache = options.useCache;
var defaultState = __assign({}, common_1.initState);
if (!skip && !refData.isInit) {
if (cacheData && !refData.hasFed && (isSSR || useCache !== false)) {
var response = cacheData.response, error = cacheData.error;
var action = {
type: common_1.ACTIONS.REQUEST_END,
response: response,
error: error,
options: options,
};
debug && console.log('[ReactUseApi][Feed]', cacheKey);
if (!isSSR) {
action.fromCache = true;
refData.hasFed = true;
}
defaultState = exports.reducer(defaultState, action);
}
else if (isSSR) {
if (!cacheKeys.has(cacheKey)) {
cacheKeys.add(cacheKey);
debug && console.log('[ReactUseApi][Collect]', cacheKey);
}
ssrConfigs.push({
config: config,
cacheKey: cacheKey,
});
}
}
refData.isInit = true;
var _c = react_1.useReducer(exports.reducer, defaultState), state = _c[0], dispatch = _c[1];
var shouldRequest = options.shouldRequest, watch = options.watch;
var loading = state.loading, data = state.data;
var request = react_1.useCallback(function (cfg, keepState, revalidate) {
if (cfg === void 0) { cfg = refData.config; }
if (keepState === void 0) { keepState = false; }
if (revalidate === void 0) { revalidate = true; }
return __awaiter(_this, void 0, void 0, function () {
var _a, _b;
return __generator(this, function (_c) {
if (options.skip) {
return [2 /*return*/, null];
}
// foolproof
if (((_a = cfg) === null || _a === void 0 ? void 0 : _a.target) && ((_b = cfg) === null || _b === void 0 ? void 0 : _b.isDefaultPrevented)) {
cfg = refData.config;
}
// update state's cachekey for saving the prevState when requesting (refreshing)
// it's good to set true for pagination
if (keepState) {
state.$cacheKey = cacheKey;
}
refData.isRequesting = true;
return [2 /*return*/, exports.fetchApi(context, cfg, options, dispatch, revalidate)];
});
});
}, [context, cacheKey, options, dispatch, state, refData]);
var shouldFetchApi = react_1.useCallback(function (forRerender) {
if (forRerender === void 0) { forRerender = false; }
var shouldRequestResult;
if (common_1.isFunction(shouldRequest)) {
shouldRequestResult = !!shouldRequest();
}
return (!skip &&
!refData.isRequesting &&
((forRerender
? shouldRequestResult === true
: // false means skip
shouldRequestResult !== false) ||
hasChangedConfig));
}, [skip, refData, shouldRequest, hasChangedConfig]);
// for each re-rendering
if (shouldFetchApi(true)) {
refData.refreshFlag = Date.now();
}
if (!loading && refData.isRequesting) {
refData.isRequesting = false;
}
var useIsomorphicLayoutEffect = isSSR ? react_1.useEffect : react_1.useLayoutEffect;
var effect = react_1.useCallback(function (callback) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var wrapper = function () {
if (isValidConfig) {
return callback();
}
};
return useIsomorphicLayoutEffect.apply(void 0, __spreadArrays([wrapper], args));
}, [isValidConfig]);
effect(function () {
// SSR will never invoke request() due to the following cases:
// 1. There is a cacheData for the cacheKey
// 2. Feeding the data come from the cache (using defaultState)
// 3. Calling API forcibly due to useCache=false
// For non-SSR, cacheData will be undefined if cacheKey has been changed
if (!isSSR && !refData.hasFed) {
request(undefined, undefined, false);
}
}, __spreadArrays([
cacheKey,
useCache,
refData,
refData.refreshFlag
], (Array.isArray(watch) ? watch : [])));
if (!isValidConfig) {
return [undefined, undefined, undefined];
}
return [data, state, request];
}
exports.useApi = useApi;
exports.reducer = function (state, action) {
var type = action.type, options = action.options;
var cacheKey = options.$cacheKey;
var basicState = {
$cacheKey: cacheKey,
};
switch (type) {
case common_1.ACTIONS.REQUEST_START: {
return __assign(__assign(__assign({}, (cacheKey !== state.$cacheKey ? common_1.initState : state)), { loading: true, error: undefined, fromCache: false }), basicState);
}
case common_1.ACTIONS.REQUEST_END: {
var response = action.response, error = action.error, fromCache = action.fromCache;
var pre = state.prevState, prevState = __rest(state, ["prevState"]);
var prevData = prevState.data;
var dependencies = options.dependencies, $hasChangedConfig = options.$hasChangedConfig;
var newState = __assign(__assign(__assign({}, prevState), { prevData: prevData,
prevState: prevState, loading: false, response: response,
dependencies: dependencies,
error: error, fromCache: !!fromCache }), basicState);
if ($hasChangedConfig) {
delete newState.prevState;
delete newState.prevData;
}
newState.data = error ? undefined : common_1.getResponseData(options, newState);
return newState;
}
default:
return state;
}
};
exports.fetchApi = function (context, config, options, dispatch, revalidate) {
if (revalidate === void 0) { revalidate = false; }
return __awaiter(void 0, void 0, void 0, function () {
var cache, cacheKey, useCache, promiseKey, _a, response, error, cacheData, promise, fromCache, err_1;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
cache = context.settings.cache;
cacheKey = options.$cacheKey, useCache = options.useCache;
promiseKey = cacheKey + "::promise";
_a = {}, response = _a.response, error = _a.error;
_b.label = 1;
case 1:
_b.trys.push([1, 11, 12, 13]);
cacheData = cache.get(cacheKey);
promise = cache.get(promiseKey);
response = cacheData === null || cacheData === void 0 ? void 0 : cacheData.response;
error = cacheData === null || cacheData === void 0 ? void 0 : cacheData.error;
fromCache = !revalidate;
if (!revalidate) return [3 /*break*/, 3];
dispatch({ type: common_1.ACTIONS.REQUEST_START, options: options });
return [4 /*yield*/, common_1.axiosAll(context, config)];
case 2:
response = _b.sent();
return [3 /*break*/, 10];
case 3:
if (!(useCache !== false && promise)) return [3 /*break*/, 5];
dispatch({ type: common_1.ACTIONS.REQUEST_START, options: options });
return [4 /*yield*/, promise];
case 4:
response = _b.sent();
return [3 /*break*/, 10];
case 5:
if (!(useCache !== false && (response || error))) return [3 /*break*/, 6];
return [3 /*break*/, 10];
case 6:
if (!useCache) return [3 /*break*/, 8];
dispatch({ type: common_1.ACTIONS.REQUEST_START, options: options });
promise = common_1.axiosAll(context, config);
// save promise for next coming hooks
cache.set(promiseKey, promise);
return [4 /*yield*/, promise];
case 7:
response = _b.sent();
return [3 /*break*/, 10];
case 8:
fromCache = false;
dispatch({ type: common_1.ACTIONS.REQUEST_START, options: options });
return [4 /*yield*/, common_1.axiosAll(context, config)];
case 9:
response = _b.sent();
_b.label = 10;
case 10:
dispatch({ type: common_1.ACTIONS.REQUEST_END, response: response, error: error, options: options, fromCache: fromCache });
return [3 /*break*/, 13];
case 11:
err_1 = _b.sent();
error = err_1;
dispatch({
type: common_1.ACTIONS.REQUEST_END,
error: error,
options: options,
});
return [3 /*break*/, 13];
case 12:
// save the data if there is no cache data come from server
if (useCache) {
if (!cache.has(cacheKey)) {
cache.set(cacheKey, {
response: response,
error: error,
});
}
if (cache.has(promiseKey)) {
cache.del(promiseKey);
}
}
return [7 /*endfinally*/];
case 13: return [2 /*return*/];
}
});
});
};
exports.handleUseApiOptions = function (opt, settings, config, cacheKey) {
var options = common_1.isObject(opt)
? __assign({}, opt)
: {
watch: Array.isArray(opt) ? opt : [],
handleData: common_1.isFunction(opt) ? opt : undefined,
};
options.$cacheKey = cacheKey;
var alwaysUseCache = settings.alwaysUseCache, shouldUseApiCache = settings.shouldUseApiCache;
var globalUseCache = shouldUseApiCache(config, cacheKey) !== false;
if (alwaysUseCache) {
options.useCache = true;
}
else if (globalUseCache === false) {
options.useCache = false;
}
return options;
};
exports.verifyConfig = function (config) {
return common_1.isObject(config)
? !!config.url
: Array.isArray(config)
? config.every(function (each) { return !!each.url; })
: false;
};
exports.default = useApi;
//# sourceMappingURL=useApi.js.map