UNPKG

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
"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 __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