Comparing version 1.0.7 to 1.0.8
@@ -17,2 +17,4 @@ "use strict"; | ||
exports.useFetchArgsDefaults = { | ||
host: '', | ||
path: undefined, | ||
customOptions: { | ||
@@ -26,3 +28,2 @@ cacheLife: 0, | ||
onTimeout: function () { }, | ||
path: '', | ||
perPage: 0, | ||
@@ -36,3 +37,5 @@ persist: false, | ||
timeout: 0, | ||
url: '', | ||
// defaults | ||
data: undefined, | ||
loading: false | ||
}, | ||
@@ -44,6 +47,2 @@ requestInit: { | ||
}, | ||
defaults: { | ||
data: undefined, | ||
loading: false | ||
}, | ||
dependencies: undefined | ||
@@ -50,0 +49,0 @@ }; |
import { HTTPMethod, Interceptors, ValueOf, DoFetchArgs, Cache } from './types'; | ||
export default function doFetchArgs<TData = any>(initialOptions: RequestInit, initialURL: string, path: string, method: HTTPMethod, controller: AbortController, cacheLife: number, cache: Cache, routeOrBody?: string | BodyInit | object, bodyAs2ndParam?: BodyInit | object, requestInterceptor?: ValueOf<Pick<Interceptors, 'request'>>): Promise<DoFetchArgs>; | ||
export default function doFetchArgs<TData = any>(initialOptions: RequestInit, method: HTTPMethod, controller: AbortController, cacheLife: number, cache: Cache, host?: string, path?: string, routeOrBody?: string | BodyInit | object, bodyAs2ndParam?: BodyInit | object, requestInterceptor?: ValueOf<Pick<Interceptors, 'request'>>): Promise<DoFetchArgs>; |
@@ -53,3 +53,3 @@ "use strict"; | ||
var GET = types_1.HTTPMethod.GET; | ||
function doFetchArgs(initialOptions, initialURL, path, method, controller, cacheLife, cache, routeOrBody, bodyAs2ndParam, requestInterceptor) { | ||
function doFetchArgs(initialOptions, method, controller, cacheLife, cache, host, path, routeOrBody, bodyAs2ndParam, requestInterceptor) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
@@ -71,3 +71,3 @@ var route, url, body, headers, options, responseID, _a, _b; | ||
})(); | ||
url = "" + initialURL + path + route; | ||
url = "" + host + utils_1.addSlash(path, host) + utils_1.addSlash(route); | ||
body = (function () { | ||
@@ -121,3 +121,3 @@ // FormData instanceof check should go first, because React Native's FormData implementation | ||
if (!requestInterceptor) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, requestInterceptor({ options: opts, url: initialURL, path: path, route: route })]; | ||
return [4 /*yield*/, requestInterceptor({ options: opts, url: host, path: path, route: route })]; | ||
case 1: | ||
@@ -124,0 +124,0 @@ interceptor = _a.sent(); |
@@ -73,3 +73,3 @@ import { ReactNode } from 'react'; | ||
url: string; | ||
options: Options; | ||
options: IncomingOptions; | ||
graphql?: boolean; | ||
@@ -79,3 +79,3 @@ } | ||
url?: string; | ||
options?: Options; | ||
options?: IncomingOptions; | ||
graphql?: boolean; | ||
@@ -125,3 +125,3 @@ children: ReactNode; | ||
export declare type Req<TData = any> = ReqMethods & ReqBase<TData>; | ||
export declare type UseFetchArgs = [(string | OptionsMaybeURL | OverwriteGlobalOptions)?, (NoUrlOptions | OverwriteGlobalOptions | any[])?, any[]?]; | ||
export declare type UseFetchArgs = [(string | IncomingOptions | OverwriteGlobalOptions)?, (IncomingOptions | OverwriteGlobalOptions | any[])?, any[]?]; | ||
export declare type UseFetchArrayReturn<TData> = [Req<TData>, Res<TData>, boolean, Error]; | ||
@@ -135,7 +135,7 @@ export declare type UseFetchObjectReturn<TData> = ReqBase<TData> & ReqMethods & { | ||
request?: ({ options, url, path, route }: { | ||
options: Options; | ||
url: string; | ||
path: string; | ||
route: string; | ||
}) => Promise<Options> | Options; | ||
options: RequestInit; | ||
url?: string; | ||
path?: string; | ||
route?: string; | ||
}) => Promise<RequestInit> | RequestInit; | ||
response?: ({ response }: { | ||
@@ -153,29 +153,26 @@ response: Res<TData>; | ||
export interface CustomOptions { | ||
cacheLife?: number; | ||
cachePolicy?: CachePolicies; | ||
data?: any; | ||
interceptors?: Interceptors; | ||
loading?: boolean; | ||
onAbort?: () => void; | ||
onError?: OnError; | ||
onNewData?: (currData: any, newData: any) => any; | ||
onTimeout?: () => void; | ||
path?: string; | ||
persist?: boolean; | ||
perPage?: number; | ||
responseType?: ResponseType; | ||
retries?: number; | ||
retryOn?: RetryOn; | ||
retryDelay?: RetryDelay; | ||
suspense?: boolean; | ||
timeout?: number; | ||
url?: string; | ||
cacheLife: number; | ||
cachePolicy: CachePolicies; | ||
data: any; | ||
interceptors: Interceptors; | ||
loading: boolean; | ||
onAbort: () => void; | ||
onError: OnError; | ||
onNewData: (currData: any, newData: any) => any; | ||
onTimeout: () => void; | ||
persist: boolean; | ||
perPage: number; | ||
responseType: ResponseType; | ||
retries: number; | ||
retryOn: RetryOn; | ||
retryDelay: RetryDelay; | ||
suspense: boolean; | ||
timeout: number; | ||
} | ||
export declare type IncomingOptions = Partial<CustomOptions> & Omit<RequestInit, 'body'> & { | ||
body?: BodyInit | object | null; | ||
}; | ||
export declare type Options = CustomOptions & Omit<RequestInit, 'body'> & { | ||
body?: BodyInit | object | null; | ||
}; | ||
export declare type NoUrlOptions = Omit<Options, 'url'>; | ||
export declare type OptionsMaybeURL = NoUrlOptions & Partial<Pick<Options, 'url'>> & { | ||
url?: string; | ||
}; | ||
export declare type OverwriteGlobalOptions = (options: Options) => Options; | ||
@@ -190,2 +187,4 @@ export declare type RetryOn = (<TData = any>({ attempt, error, response }: RetryOpts) => Promise<boolean>) | number[]; | ||
export declare type UseFetchArgsReturn = { | ||
host: string; | ||
path?: string; | ||
customOptions: { | ||
@@ -199,3 +198,2 @@ cacheLife: number; | ||
onTimeout: () => void; | ||
path: string; | ||
perPage: number; | ||
@@ -209,9 +207,6 @@ persist: boolean; | ||
timeout: number; | ||
url: string; | ||
}; | ||
requestInit: RequestInit; | ||
defaults: { | ||
loading: boolean; | ||
data?: any; | ||
}; | ||
requestInit: RequestInit; | ||
dependencies?: any[]; | ||
@@ -218,0 +213,0 @@ }; |
@@ -49,2 +49,13 @@ "use strict"; | ||
}; | ||
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 __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -69,5 +80,5 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
} | ||
var _a = useFetchArgs_1.default.apply(void 0, args), customOptions = _a.customOptions, requestInit = _a.requestInit, defaults = _a.defaults, dependencies = _a.dependencies; | ||
var _a = useFetchArgs_1.default.apply(void 0, args), host = _a.host, path = _a.path, customOptions = _a.customOptions, requestInit = _a.requestInit, dependencies = _a.dependencies; | ||
var cacheLife = customOptions.cacheLife, cachePolicy = customOptions.cachePolicy, // 'cache-first' by default | ||
interceptors = customOptions.interceptors, onAbort = customOptions.onAbort, onError = customOptions.onError, onNewData = customOptions.onNewData, onTimeout = customOptions.onTimeout, path = customOptions.path, perPage = customOptions.perPage, persist = customOptions.persist, responseType = customOptions.responseType, retries = customOptions.retries, retryDelay = customOptions.retryDelay, retryOn = customOptions.retryOn, suspense = customOptions.suspense, timeout = customOptions.timeout, initialURL = customOptions.url; | ||
interceptors = customOptions.interceptors, onAbort = customOptions.onAbort, onError = customOptions.onError, onNewData = customOptions.onNewData, onTimeout = customOptions.onTimeout, perPage = customOptions.perPage, persist = customOptions.persist, responseType = customOptions.responseType, retries = customOptions.retries, retryDelay = customOptions.retryDelay, retryOn = customOptions.retryOn, suspense = customOptions.suspense, timeout = customOptions.timeout, defaults = __rest(customOptions, ["cacheLife", "cachePolicy", "interceptors", "onAbort", "onError", "onNewData", "onTimeout", "perPage", "persist", "responseType", "retries", "retryDelay", "retryOn", "suspense", "timeout"]); | ||
var cache = useCache_1.default({ persist: persist, cacheLife: cacheLife, cachePolicy: cachePolicy }); | ||
@@ -89,5 +100,5 @@ var isServer = use_ssr_1.default().isServer; | ||
var doFetch = function (routeOrBody, body) { return __awaiter(_this, void 0, void 0, function () { | ||
var theController, _a, url, options, response, theData, _b, _c, err_1, timer, newData, newRes, _d, _e, opts, shouldRetry, _f, _g, theData, err_2, opts, shouldRetry, _h, _j, theData; | ||
return __generator(this, function (_k) { | ||
switch (_k.label) { | ||
var theController, _a, url, options, response, timer, newData, newRes, _b, _c, opts, shouldRetry, _d, _e, theData, err_1, opts, shouldRetry, _f, _g, theData; | ||
return __generator(this, function (_h) { | ||
switch (_h.label) { | ||
case 0: | ||
@@ -99,43 +110,11 @@ if (isServer) | ||
theController = controller.current; | ||
return [4 /*yield*/, doFetchArgs_1.default(requestInit, initialURL, path, method, theController, cacheLife, cache, routeOrBody, body, interceptors.request)]; | ||
return [4 /*yield*/, doFetchArgs_1.default(requestInit, method, theController, cacheLife, cache, host, path, routeOrBody, body, interceptors.request)]; | ||
case 1: | ||
_a = _k.sent(), url = _a.url, options = _a.options, response = _a.response; | ||
_a = _h.sent(), url = _a.url, options = _a.options, response = _a.response; | ||
error.current = undefined; | ||
if (!(response.isCached && cachePolicy === CACHE_FIRST)) return [3 /*break*/, 8]; | ||
_k.label = 2; | ||
case 2: | ||
_k.trys.push([2, 7, , 8]); | ||
res.current = response.cached; | ||
return [4 /*yield*/, utils_1.tryGetData(response.cached, defaults.data, responseType)]; | ||
case 3: | ||
theData = _k.sent(); | ||
res.current.data = theData; | ||
_b = res; | ||
if (!interceptors.response) return [3 /*break*/, 5]; | ||
return [4 /*yield*/, interceptors.response({ response: res.current })]; | ||
case 4: | ||
_c = _k.sent(); | ||
return [3 /*break*/, 6]; | ||
case 5: | ||
_c = res.current; | ||
_k.label = 6; | ||
case 6: | ||
_b.current = _c; | ||
utils_1.invariant('data' in res.current, 'You must have `data` field on the Response returned from your `interceptors.response`'); | ||
data.current = res.current.data; | ||
if (!suspense && mounted.current) | ||
forceUpdate(); | ||
return [2 /*return*/, data.current]; | ||
case 7: | ||
err_1 = _k.sent(); | ||
error.current = err_1; | ||
if (mounted.current) | ||
forceUpdate(); | ||
return [3 /*break*/, 8]; | ||
case 8: | ||
if (!suspense) | ||
setLoading(true); | ||
// don't perform the request if there is no more data to fetch (pagination) | ||
if (perPage > 0 && !hasMore.current && !error.current) | ||
return [2 /*return*/, data.current]; | ||
if (!suspense) | ||
setLoading(true); | ||
timer = timeout && setTimeout(function () { | ||
@@ -147,24 +126,29 @@ timedout.current = true; | ||
}, timeout); | ||
_k.label = 9; | ||
case 9: | ||
_k.trys.push([9, 22, 28, 29]); | ||
return [4 /*yield*/, fetch(url, options)]; | ||
case 10: | ||
newRes = _k.sent(); | ||
_h.label = 2; | ||
case 2: | ||
_h.trys.push([2, 17, 23, 24]); | ||
if (!(response.isCached && cachePolicy === CACHE_FIRST)) return [3 /*break*/, 3]; | ||
newRes = response.cached; | ||
return [3 /*break*/, 5]; | ||
case 3: return [4 /*yield*/, fetch(url, options)]; | ||
case 4: | ||
newRes = _h.sent(); | ||
_h.label = 5; | ||
case 5: | ||
res.current = newRes.clone(); | ||
return [4 /*yield*/, utils_1.tryGetData(newRes, defaults.data, responseType)]; | ||
case 11: | ||
newData = _k.sent(); | ||
case 6: | ||
newData = _h.sent(); | ||
res.current.data = onNewData(data.current, newData); | ||
_d = res; | ||
if (!interceptors.response) return [3 /*break*/, 13]; | ||
_b = res; | ||
if (!interceptors.response) return [3 /*break*/, 8]; | ||
return [4 /*yield*/, interceptors.response({ response: res.current })]; | ||
case 12: | ||
_e = _k.sent(); | ||
return [3 /*break*/, 14]; | ||
case 13: | ||
_e = res.current; | ||
_k.label = 14; | ||
case 14: | ||
_d.current = _e; | ||
case 7: | ||
_c = _h.sent(); | ||
return [3 /*break*/, 9]; | ||
case 8: | ||
_c = res.current; | ||
_h.label = 9; | ||
case 9: | ||
_b.current = _c; | ||
utils_1.invariant('data' in res.current, 'You must have `data` field on the Response returned from your `interceptors.response`'); | ||
@@ -175,68 +159,68 @@ data.current = res.current.data; | ||
// automatically retry on fail until attempts run out | ||
_f = !utils_1.isFunction(retryOn) && Array.isArray(retryOn) && retryOn.length < 1 && (newRes === null || newRes === void 0 ? void 0 : newRes.ok) === false | ||
_d = !utils_1.isFunction(retryOn) && Array.isArray(retryOn) && retryOn.length < 1 && (newRes === null || newRes === void 0 ? void 0 : newRes.ok) === false | ||
// otherwise only retry when is specified | ||
|| Array.isArray(retryOn) && retryOn.includes(newRes.status); | ||
if (_f) | ||
if (_d) | ||
// if we just have `retries` set with NO `retryOn` then | ||
// automatically retry on fail until attempts run out | ||
return [3 /*break*/, 17]; | ||
_g = utils_1.isFunction(retryOn); | ||
if (!_g) return [3 /*break*/, 16]; | ||
return [3 /*break*/, 12]; | ||
_e = utils_1.isFunction(retryOn); | ||
if (!_e) return [3 /*break*/, 11]; | ||
return [4 /*yield*/, retryOn(opts)]; | ||
case 15: | ||
_g = (_k.sent()); | ||
_k.label = 16; | ||
case 16: | ||
_f = _g; | ||
_k.label = 17; | ||
case 17: | ||
shouldRetry = (_f) && retries > 0 && retries > attempt.current; | ||
if (!shouldRetry) return [3 /*break*/, 19]; | ||
case 10: | ||
_e = (_h.sent()); | ||
_h.label = 11; | ||
case 11: | ||
_d = _e; | ||
_h.label = 12; | ||
case 12: | ||
shouldRetry = (_d) && retries > 0 && retries > attempt.current; | ||
if (!shouldRetry) return [3 /*break*/, 14]; | ||
return [4 /*yield*/, retry(opts, routeOrBody, body)]; | ||
case 18: | ||
theData = _k.sent(); | ||
case 13: | ||
theData = _h.sent(); | ||
return [2 /*return*/, theData]; | ||
case 19: | ||
if (!(cachePolicy === CACHE_FIRST)) return [3 /*break*/, 21]; | ||
case 14: | ||
if (!(cachePolicy === CACHE_FIRST)) return [3 /*break*/, 16]; | ||
return [4 /*yield*/, cache.set(response.id, newRes.clone())]; | ||
case 20: | ||
_k.sent(); | ||
_k.label = 21; | ||
case 21: | ||
case 15: | ||
_h.sent(); | ||
_h.label = 16; | ||
case 16: | ||
if (Array.isArray(data.current) && !!(data.current.length % perPage)) | ||
hasMore.current = false; | ||
return [3 /*break*/, 29]; | ||
case 22: | ||
err_2 = _k.sent(); | ||
return [3 /*break*/, 24]; | ||
case 17: | ||
err_1 = _h.sent(); | ||
if (attempt.current >= retries && timedout.current) | ||
error.current = utils_1.makeError('AbortError', 'Timeout Error'); | ||
opts = { attempt: attempt.current, error: err_2 }; | ||
opts = { attempt: attempt.current, error: err_1 }; | ||
// if we just have `retries` set with NO `retryOn` then | ||
// automatically retry on fail until attempts run out | ||
_h = !utils_1.isFunction(retryOn) && Array.isArray(retryOn) && retryOn.length < 1; | ||
if (_h) | ||
_f = !utils_1.isFunction(retryOn) && Array.isArray(retryOn) && retryOn.length < 1; | ||
if (_f) | ||
// if we just have `retries` set with NO `retryOn` then | ||
// automatically retry on fail until attempts run out | ||
return [3 /*break*/, 25]; | ||
_j = utils_1.isFunction(retryOn); | ||
if (!_j) return [3 /*break*/, 24]; | ||
return [3 /*break*/, 20]; | ||
_g = utils_1.isFunction(retryOn); | ||
if (!_g) return [3 /*break*/, 19]; | ||
return [4 /*yield*/, retryOn(opts)]; | ||
case 23: | ||
_j = (_k.sent()); | ||
_k.label = 24; | ||
case 24: | ||
_h = _j; | ||
_k.label = 25; | ||
case 25: | ||
shouldRetry = (_h) && retries > 0 && retries > attempt.current; | ||
if (!shouldRetry) return [3 /*break*/, 27]; | ||
case 18: | ||
_g = (_h.sent()); | ||
_h.label = 19; | ||
case 19: | ||
_f = _g; | ||
_h.label = 20; | ||
case 20: | ||
shouldRetry = (_f) && retries > 0 && retries > attempt.current; | ||
if (!shouldRetry) return [3 /*break*/, 22]; | ||
return [4 /*yield*/, retry(opts, routeOrBody, body)]; | ||
case 26: | ||
theData = _k.sent(); | ||
case 21: | ||
theData = _h.sent(); | ||
return [2 /*return*/, theData]; | ||
case 27: | ||
if (err_2.name !== 'AbortError') | ||
error.current = utils_1.makeError(err_2.name, err_2.message); | ||
return [3 /*break*/, 29]; | ||
case 28: | ||
case 22: | ||
if (err_1.name !== 'AbortError') | ||
error.current = utils_1.makeError(err_1.name, err_1.message); | ||
return [3 /*break*/, 24]; | ||
case 23: | ||
timedout.current = false; | ||
@@ -247,3 +231,3 @@ if (timer) | ||
return [7 /*endfinally*/]; | ||
case 29: | ||
case 24: | ||
if (newRes && !newRes.ok && !error.current) | ||
@@ -311,3 +295,3 @@ error.current = utils_1.makeError(newRes.status, newRes.statusText); | ||
return doFetch; | ||
}, [isServer, onAbort, requestInit, initialURL, path, interceptors, cachePolicy, perPage, timeout, persist, cacheLife, onTimeout, defaults.data, onNewData, forceUpdate, suspense]); | ||
}, [isServer, onAbort, requestInit, host, path, interceptors, cachePolicy, perPage, timeout, persist, cacheLife, onTimeout, defaults.data, onNewData, forceUpdate, suspense]); | ||
var post = react_1.useCallback(makeFetch(types_1.HTTPMethod.POST), [makeFetch]); | ||
@@ -314,0 +298,0 @@ var del = react_1.useCallback(makeFetch(types_1.HTTPMethod.DELETE), [makeFetch]); |
@@ -1,2 +0,2 @@ | ||
import { OptionsMaybeURL, NoUrlOptions, OverwriteGlobalOptions, UseFetchArgsReturn } from './types'; | ||
export default function useFetchArgs(urlOrOptionsOrOverwriteGlobal?: string | OptionsMaybeURL | OverwriteGlobalOptions, optionsNoURLsOrOverwriteGlobalOrDeps?: NoUrlOptions | OverwriteGlobalOptions | any[], deps?: any[]): UseFetchArgsReturn; | ||
import { OverwriteGlobalOptions, IncomingOptions, UseFetchArgsReturn } from './types'; | ||
export default function useFetchArgs(urlOrPathOrOptionsOrOverwriteGlobalOptions?: string | IncomingOptions | OverwriteGlobalOptions, optionsOrOverwriteGlobalOrDeps?: IncomingOptions | OverwriteGlobalOptions | any[], deps?: any[]): UseFetchArgsReturn; |
@@ -16,2 +16,9 @@ "use strict"; | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -21,126 +28,75 @@ var utils_1 = require("./utils"); | ||
var FetchContext_1 = __importDefault(require("./FetchContext")); | ||
var defaults_1 = __importDefault(require("./defaults")); | ||
var useField = function (field, urlOrOptions, optionsNoURLs) { | ||
var defaults_1 = __importStar(require("./defaults")); | ||
function useFetchArgs(urlOrPathOrOptionsOrOverwriteGlobalOptions, optionsOrOverwriteGlobalOrDeps, deps) { | ||
utils_1.invariant(!(utils_1.isObject(urlOrPathOrOptionsOrOverwriteGlobalOptions) && utils_1.isObject(optionsOrOverwriteGlobalOrDeps)), 'You cannot have a 2nd parameter of useFetch as object when your first argument is an object.'); | ||
var context = react_1.useContext(FetchContext_1.default); | ||
var contextOptions = context.options || {}; | ||
return react_1.useMemo(function () { | ||
if (utils_1.isObject(urlOrOptions) && field in urlOrOptions) | ||
return urlOrOptions[field]; | ||
if (utils_1.isObject(optionsNoURLs) && field in optionsNoURLs) { | ||
return optionsNoURLs[field]; | ||
} | ||
if (field in contextOptions) | ||
return contextOptions[field]; | ||
return defaults_1.default[field]; | ||
}, [urlOrOptions, field, optionsNoURLs, contextOptions]); | ||
}; | ||
function useFetchArgs(urlOrOptionsOrOverwriteGlobal, optionsNoURLsOrOverwriteGlobalOrDeps, deps) { | ||
var context = react_1.useContext(FetchContext_1.default); | ||
context.options = react_1.useMemo(function () { | ||
var overwriteGlobalOptions = (utils_1.isFunction(urlOrOptionsOrOverwriteGlobal) ? urlOrOptionsOrOverwriteGlobal : utils_1.isFunction(optionsNoURLsOrOverwriteGlobalOrDeps) && optionsNoURLsOrOverwriteGlobalOrDeps); | ||
if (!overwriteGlobalOptions) | ||
return context.options; | ||
// make a copy so we make sure not to modify the original context | ||
return overwriteGlobalOptions(__assign({}, context.options)); | ||
}, [context.options, optionsNoURLsOrOverwriteGlobalOrDeps, urlOrOptionsOrOverwriteGlobal]); | ||
var urlOrOptions = urlOrOptionsOrOverwriteGlobal; | ||
var optionsNoURLs = optionsNoURLsOrOverwriteGlobalOrDeps; | ||
utils_1.invariant(!(utils_1.isObject(urlOrOptions) && utils_1.isObject(optionsNoURLs)), 'You cannot have a 2nd parameter of useFetch when your first argument is an object config.'); | ||
var url = react_1.useMemo(function () { | ||
if (utils_1.isString(urlOrOptions) && urlOrOptions) | ||
return urlOrOptions; | ||
if (utils_1.isObject(urlOrOptions) && !!urlOrOptions.url) | ||
return urlOrOptions.url; | ||
var host = react_1.useMemo(function () { | ||
var maybeHost = urlOrPathOrOptionsOrOverwriteGlobalOptions; | ||
if (utils_1.isString(maybeHost) && maybeHost.includes('://')) | ||
return maybeHost; | ||
if (context.url) | ||
return context.url; | ||
return defaults_1.default.url; | ||
}, [context.url, urlOrOptions]); | ||
utils_1.invariant(!!url, 'The first argument of useFetch is required unless you have a global url setup like: <Provider url="https://example.com"></Provider>'); | ||
return defaults_1.default.host; | ||
}, [context.url, urlOrPathOrOptionsOrOverwriteGlobalOptions]); | ||
var path = react_1.useMemo(function () { | ||
var maybePath = urlOrPathOrOptionsOrOverwriteGlobalOptions; | ||
if (utils_1.isString(maybePath) && !maybePath.includes('://')) | ||
return maybePath; | ||
}, [urlOrPathOrOptionsOrOverwriteGlobalOptions]); | ||
var overwriteGlobalOptions = react_1.useMemo(function () { | ||
if (utils_1.isFunction(urlOrPathOrOptionsOrOverwriteGlobalOptions)) | ||
return urlOrPathOrOptionsOrOverwriteGlobalOptions; | ||
if (utils_1.isFunction(optionsOrOverwriteGlobalOrDeps)) | ||
return optionsOrOverwriteGlobalOrDeps; | ||
}, []); | ||
var options = react_1.useMemo(function () { | ||
var localOptions = { headers: {} }; | ||
if (utils_1.isObject(urlOrPathOrOptionsOrOverwriteGlobalOptions)) { | ||
localOptions = urlOrPathOrOptionsOrOverwriteGlobalOptions; | ||
} | ||
else if (utils_1.isObject(optionsOrOverwriteGlobalOrDeps)) { | ||
localOptions = optionsOrOverwriteGlobalOrDeps; | ||
} | ||
var globalOptions = context.options; | ||
var finalOptions = __assign(__assign(__assign(__assign({}, defaults_1.default), globalOptions), localOptions), { headers: __assign(__assign(__assign({}, defaults_1.default.headers), globalOptions.headers), localOptions.headers) }); | ||
if (overwriteGlobalOptions) | ||
return overwriteGlobalOptions(finalOptions); | ||
return finalOptions; | ||
}, [urlOrPathOrOptionsOrOverwriteGlobalOptions, overwriteGlobalOptions, context.options]); | ||
var requestInit = react_1.useMemo(function () { return utils_1.pullOutRequestInit(options); }, [options]); | ||
var dependencies = react_1.useMemo(function () { | ||
if (Array.isArray(optionsNoURLsOrOverwriteGlobalOrDeps)) | ||
return optionsNoURLsOrOverwriteGlobalOrDeps; | ||
if (Array.isArray(optionsOrOverwriteGlobalOrDeps)) | ||
return optionsOrOverwriteGlobalOrDeps; | ||
if (Array.isArray(deps)) | ||
return deps; | ||
return defaults_1.default.dependencies; | ||
}, [optionsNoURLsOrOverwriteGlobalOrDeps, deps]); | ||
var data = useField('data', urlOrOptions, optionsNoURLs); | ||
var cacheLife = useField('cacheLife', urlOrOptions, optionsNoURLs); | ||
}, [optionsOrOverwriteGlobalOrDeps, deps]); | ||
var cacheLife = options.cacheLife, retries = options.retries, retryDelay = options.retryDelay, retryOn = options.retryOn; | ||
utils_1.invariant(Number.isInteger(cacheLife) && cacheLife >= 0, '`cacheLife` must be a number >= 0'); | ||
var cachePolicy = useField('cachePolicy', urlOrOptions, optionsNoURLs); | ||
var onAbort = useField('onAbort', urlOrOptions, optionsNoURLs); | ||
var onError = useField('onError', urlOrOptions, optionsNoURLs); | ||
var onNewData = useField('onNewData', urlOrOptions, optionsNoURLs); | ||
var onTimeout = useField('onTimeout', urlOrOptions, optionsNoURLs); | ||
var path = useField('path', urlOrOptions, optionsNoURLs); | ||
var perPage = useField('perPage', urlOrOptions, optionsNoURLs); | ||
var persist = useField('persist', urlOrOptions, optionsNoURLs); | ||
var responseType = useField('responseType', urlOrOptions, optionsNoURLs); | ||
var retries = useField('retries', urlOrOptions, optionsNoURLs); | ||
utils_1.invariant(Number.isInteger(retries) && retries >= 0, '`retries` must be a number >= 0'); | ||
var retryDelay = useField('retryDelay', urlOrOptions, optionsNoURLs); | ||
utils_1.invariant(utils_1.isFunction(retryDelay) || Number.isInteger(retryDelay) && retryDelay >= 0, '`retryDelay` must be a positive number or a function returning a positive number.'); | ||
var retryOn = useField('retryOn', urlOrOptions, optionsNoURLs); | ||
var isValidRetryOn = utils_1.isFunction(retryOn) || (Array.isArray(retryOn) && retryOn.every(utils_1.isPositiveNumber)); | ||
utils_1.invariant(isValidRetryOn, '`retryOn` must be an array of positive numbers or a function returning a boolean.'); | ||
var suspense = useField('suspense', urlOrOptions, optionsNoURLs); | ||
var timeout = useField('timeout', urlOrOptions, optionsNoURLs); | ||
var loading = react_1.useMemo(function () { | ||
if (utils_1.isObject(urlOrOptions)) | ||
return !!urlOrOptions.loading || Array.isArray(dependencies); | ||
if (utils_1.isObject(optionsNoURLs)) | ||
return !!optionsNoURLs.loading || Array.isArray(dependencies); | ||
return defaults_1.default.loading || Array.isArray(dependencies); | ||
}, [urlOrOptions, dependencies, optionsNoURLs]); | ||
var loading = options.loading || Array.isArray(dependencies); | ||
var interceptors = react_1.useMemo(function () { | ||
var contextInterceptors = context.options && (context.options.interceptors || {}); | ||
var final = __assign({}, contextInterceptors); | ||
if (utils_1.isObject(urlOrOptions) && utils_1.isObject(urlOrOptions.interceptors)) { | ||
if (urlOrOptions.interceptors.request) | ||
final.request = urlOrOptions.interceptors.request; | ||
if (urlOrOptions.interceptors.response) | ||
final.response = urlOrOptions.interceptors.response; | ||
} | ||
if (utils_1.isObject(optionsNoURLs) && utils_1.isObject(optionsNoURLs.interceptors)) { | ||
if (optionsNoURLs.interceptors.request) | ||
final.request = optionsNoURLs.interceptors.request; | ||
if (optionsNoURLs.interceptors.response) | ||
final.response = optionsNoURLs.interceptors.response; | ||
} | ||
var final = {}; | ||
if ('request' in options.interceptors) | ||
final.request = options.interceptors.request; | ||
if ('response' in options.interceptors) | ||
final.response = options.interceptors.response; | ||
return final; | ||
}, [context.options, urlOrOptions, optionsNoURLs]); | ||
var requestInit = react_1.useMemo(function () { | ||
var contextRequestInit = utils_1.pullOutRequestInit(context.options); | ||
var requestInitOptions = utils_1.isObject(urlOrOptions) | ||
? urlOrOptions | ||
: utils_1.isObject(optionsNoURLs) | ||
? optionsNoURLs | ||
: {}; | ||
var requestInit = utils_1.pullOutRequestInit(requestInitOptions); | ||
return __assign(__assign(__assign({}, contextRequestInit), requestInit), { headers: __assign(__assign(__assign({}, defaults_1.default.headers), contextRequestInit.headers), requestInit.headers) }); | ||
}, [context.options, urlOrOptions, optionsNoURLs]); | ||
}, [options]); | ||
var customOptions = react_1.useMemo(function () { | ||
var customOptionKeys = Object.keys(defaults_1.useFetchArgsDefaults.customOptions); // Array<keyof CustomOptions> | ||
var customOptions = customOptionKeys.reduce(function (opts, key) { | ||
opts[key] = options[key]; | ||
return opts; | ||
}, {}); | ||
return __assign(__assign({}, customOptions), { interceptors: interceptors, loading: loading }); | ||
}, [interceptors, loading]); | ||
return { | ||
customOptions: { | ||
cacheLife: cacheLife, | ||
cachePolicy: cachePolicy, | ||
interceptors: interceptors, | ||
onAbort: onAbort, | ||
onError: onError, | ||
onNewData: onNewData, | ||
onTimeout: onTimeout, | ||
path: path, | ||
persist: persist, | ||
perPage: perPage, | ||
responseType: responseType, | ||
retries: retries, | ||
retryDelay: retryDelay, | ||
retryOn: retryOn, | ||
suspense: suspense, | ||
timeout: timeout, | ||
url: url, | ||
}, | ||
host: host, | ||
path: path, | ||
customOptions: customOptions, | ||
requestInit: requestInit, | ||
defaults: { | ||
data: data, | ||
loading: loading | ||
}, | ||
dependencies: dependencies | ||
@@ -147,0 +103,0 @@ }; |
import { MutableRefObject, DependencyList } from 'react'; | ||
import { OptionsMaybeURL, Res, HTTPMethod, ResponseType } from './types'; | ||
import { Options, Res, HTTPMethod, ResponseType } from './types'; | ||
import { FunctionKeys, NonFunctionKeys } from 'utility-types'; | ||
@@ -28,3 +28,3 @@ /** | ||
*/ | ||
export declare const pullOutRequestInit: (options?: OptionsMaybeURL | undefined) => RequestInit; | ||
export declare const pullOutRequestInit: (options?: Options | undefined) => RequestInit; | ||
export declare const isEmpty: (x: any) => boolean; | ||
@@ -68,2 +68,18 @@ export declare enum Device { | ||
export declare const makeError: (name: string | number, message: string) => Error; | ||
/** | ||
* Determines if we need to add a slash to front | ||
* of a path, and adds it if we do. | ||
* Cases: | ||
* (path = '', url = '' || null | undefined) => '' | ||
* (path = '?foo=bar', url = 'a.com') => '?foo=bar' | ||
* (path = '?foo=bar', url = 'a.com/') => '?foo=bar' | ||
* (path = 'foo', url = 'a.com') => '/foo' | ||
* (path = 'foo', url = 'a.com/') => 'foo' | ||
* (path = '/foo', url = 'a.com') => '/foo' | ||
* (path = '/foo', url = 'a.com/') => 'foo' | ||
* (path = '?foo=bar') => '?foo=bar' | ||
* (path = 'foo') => '/foo' | ||
* (path = '/foo') => '/foo' | ||
*/ | ||
export declare const addSlash: (input?: string | undefined, url?: string | undefined) => string; | ||
export {}; |
@@ -183,15 +183,22 @@ "use strict"; | ||
}); }; | ||
var tryRetry = function (res, types) { return __awaiter(void 0, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
try { | ||
return [2 /*return*/, res.clone()[types[0]]()]; | ||
} | ||
catch (error) { | ||
if (types.length === 1) | ||
throw error; | ||
return [2 /*return*/, tryRetry(res.clone(), types.slice(1))]; | ||
} | ||
return [2 /*return*/]; | ||
var tryRetry = function (res, types, i) { | ||
if (i === void 0) { i = 0; } | ||
return __awaiter(void 0, void 0, void 0, function () { | ||
var error_1; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
_a.trys.push([0, 2, , 3]); | ||
return [4 /*yield*/, res.clone()[types[i]]()]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
case 2: | ||
error_1 = _a.sent(); | ||
if (types.length - 1 === i) | ||
throw error_1; | ||
return [2 /*return*/, tryRetry(res.clone(), types, ++i)]; | ||
case 3: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}); }; | ||
}; | ||
exports.responseFields = ['headers', 'ok', 'redirected', 'trailer', 'status', 'statusText', 'type', 'url', 'body', 'bodyUsed', 'data']; | ||
@@ -270,1 +277,30 @@ exports.responseMethods = ['clone', 'arrayBuffer', 'blob', 'formData', 'json', 'text']; | ||
}; | ||
/** | ||
* Determines if we need to add a slash to front | ||
* of a path, and adds it if we do. | ||
* Cases: | ||
* (path = '', url = '' || null | undefined) => '' | ||
* (path = '?foo=bar', url = 'a.com') => '?foo=bar' | ||
* (path = '?foo=bar', url = 'a.com/') => '?foo=bar' | ||
* (path = 'foo', url = 'a.com') => '/foo' | ||
* (path = 'foo', url = 'a.com/') => 'foo' | ||
* (path = '/foo', url = 'a.com') => '/foo' | ||
* (path = '/foo', url = 'a.com/') => 'foo' | ||
* (path = '?foo=bar') => '?foo=bar' | ||
* (path = 'foo') => '/foo' | ||
* (path = '/foo') => '/foo' | ||
*/ | ||
exports.addSlash = function (input, url) { | ||
if (!input) | ||
return ''; | ||
if (!url) { | ||
if (input.startsWith('?') || input.startsWith('/')) | ||
return input; | ||
return "/" + input; | ||
} | ||
if (url.endsWith('/') && input.startsWith('/')) | ||
return input.substr(1); | ||
if (!url.endsWith('/') && !input.startsWith('/') && !input.startsWith('?')) | ||
return "/" + input; | ||
return input; | ||
}; |
{ | ||
"name": "use-http", | ||
"version": "1.0.7", | ||
"version": "1.0.8", | ||
"homepage": "https://use-http.com", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -12,3 +12,3 @@ [![use-http logo][3]][5] | ||
<p align="center"> | ||
<a href="https://github.com/ava/use-http/pulls"> | ||
<a href="https://github.com/ava/use-http/blob/master/.github/contributing.md"> | ||
<img src="https://camo.githubusercontent.com/d4e0f63e9613ee474a7dfdc23c240b9795712c96/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e737667" /> | ||
@@ -93,3 +93,2 @@ </a> | ||
- useFetch - managed state, request, response, etc. [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/usefetch-request-response-managed-state-ruyi3?file=/src/index.js) [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=_-GujYZFCKI&list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW&index=6) | ||
- useFetch - route, path, Provider, etc. [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/usefetch-with-provider-c78w2) [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=JWDL_AVOYT0&list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW&index=10) | ||
- useFetch - request/response interceptors [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/usefetch-provider-requestresponse-interceptors-s1lex) [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=3HauoWh0Jts&list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW&index=8) | ||
@@ -106,3 +105,3 @@ - useFetch - retries, retryOn, retryDelay [![](https://img.shields.io/badge/example-blue.svg)](https://codesandbox.io/s/usefetch-retryon-retrydelay-s74q9) [![](https://img.shields.io/badge/video-red.svg)](https://www.youtube.com/watch?v=grE3AX-Q9ss&list=PLZIwrWkE9rCdUybd8t3tY-mUMvXkCdenW&index=9) | ||
<details open><summary><b>Basic Usage (managed state) <code>useFetch</code></b></summary> | ||
<details open><summary><b>Basic Usage Managed State <code>useFetch</code></b></summary> | ||
@@ -153,5 +152,5 @@ If the last argument of `useFetch` is not a dependency array `[]`, then it will not fire until you call one of the http methods like `get`, `post`, etc. | ||
<details><summary><b>Basic Usage (auto managed state) <code>useFetch</code></b></summary> | ||
<details open><summary><b>Basic Usage Auto-Managed State <code>useFetch</code></b></summary> | ||
This fetch is run `onMount/componentDidMount`. The last argument `[]` means it will run `onMount`. If you pass it a variable like `[someVariable]`, it will run `onMount` and again whenever `someVariable` changes values (aka `onUpdate`). **If no method is specified, GET is the default** | ||
This fetch is run `onMount/componentDidMount`. The last argument `[]` means it will run `onMount`. If you pass it a variable like `[someVariable]`, it will run `onMount` and again whenever `someVariable` changes values (aka `onUpdate`). If no method is specified, GET is the default. | ||
@@ -162,8 +161,6 @@ ```js | ||
function Todos() { | ||
// accepts all `fetch` options | ||
const options = { | ||
data: [], // setting default for `data` as array instead of undefined | ||
} | ||
const { loading, error, data } = useFetch('https://example.com/todos', options, []) // onMount (GET by default) | ||
const { loading, error, data } = useFetch('https://example.com/todos', { | ||
// these options accept all native `fetch` options | ||
data: [] // defaults the `data` to an array instead of `undefined` | ||
}, []) // <- this [] means it will fire onMount (GET by default) | ||
@@ -187,40 +184,4 @@ return ( | ||
<details open><summary><b>Suspense Mode<sup>(experimental)</sup> Auto-Managed State</b></summary> | ||
<details open><summary><b>Basic Usage (auto managed state) with <code>Provider</code></b></summary> | ||
```js | ||
import useFetch, { Provider } from 'use-http' | ||
function Todos() { | ||
const { loading, error, data } = useFetch({ | ||
path: '/todos', | ||
data: [] | ||
}, []) // onMount | ||
return ( | ||
<> | ||
{error && 'Error!'} | ||
{loading && 'Loading...'} | ||
{data.map(todo => ( | ||
<div key={todo.id}>{todo.title}</div> | ||
)} | ||
</> | ||
) | ||
} | ||
const App = () => ( | ||
<Provider url='https://example.com'> | ||
<Todos /> | ||
</Provider> | ||
) | ||
``` | ||
<!-- TODO: youtube --> | ||
<a target="_blank" rel="noopener noreferrer" href='https://codesandbox.io/s/usefetch-provider-requestresponse-interceptors-s1lex?file=/src/index.js'><img width='150px' height='30px' src='https://codesandbox.io/static/img/play-codesandbox.svg' /></a> | ||
<!-- <a target="_blank" rel="noopener noreferrer" href='XXXXXXX'><img width='150px' height='30px' src='https://github.com/ava/use-http/raw/master/public/watch-youtube-video.png' /></a> --> | ||
</details> | ||
<details open><summary><b>Suspense Mode (auto managed state)</b></summary> | ||
Can put `suspense` in 2 places. Either `useFetch` (A) or `Provider` (B). | ||
@@ -232,4 +193,3 @@ | ||
function Todos() { | ||
const { data: todos } = useFetch({ | ||
path: '/todos', | ||
const { data: todos } = useFetch('/todos', { | ||
data: [], | ||
@@ -260,5 +220,5 @@ suspense: true // A. can put `suspense: true` here | ||
<details><summary><b>Suspense Mode (managed state)</b></summary> | ||
<details><summary><b>Suspense Mode<sup>(experimental)</sup> Managed State</b></summary> | ||
Can put `suspense` in 2 places. Either `useFetch` (A) or `Provider` (B). | ||
Can put `suspense` in 2 places. Either `useFetch` (A) or `Provider` (B). Suspense mode via managed state is very experimental. | ||
@@ -339,4 +299,3 @@ ```js | ||
const { data, loading } = useFetch({ | ||
path: `/todos?page=${page}&amountPerPage=15`, | ||
const { data, loading } = useFetch(`/todos?page=${page}&amountPerPage=15`, { | ||
onNewData: (currTodos, newTodos) => [...currTodos, ...newTodos], // appends newly fetched todos | ||
@@ -442,6 +401,3 @@ perPage: 15, // stops making more requests if last todos fetched < 15 | ||
⚠️ `baseUrl` is no longer supported, it is now only `url` | ||
```jsx | ||
var request = useFetch({ url: 'https://example.com' }) | ||
// OR | ||
var request = useFetch('https://example.com') | ||
@@ -464,13 +420,11 @@ | ||
```jsx | ||
const githubRepos = useFetch({ | ||
url: `https://api.github.com/search/repositories?q=` | ||
}) | ||
const { get, abort, loading, data: repos } = useFetch('https://api.github.com/search/repositories?q=') | ||
// the line below is not isomorphic, but for simplicity we're using the browsers `encodeURI` | ||
const searchGithubRepos = e => githubRepos.get(encodeURI(e.target.value)) | ||
const searchGithubRepos = e => get(encodeURI(e.target.value)) | ||
<> | ||
<input onChange={searchGithubRepos} /> | ||
<button onClick={githubRepos.abort}>Abort</button> | ||
{githubRepos.loading ? 'Loading...' : githubRepos.data.items.map(repo => ( | ||
<button onClick={abort}>Abort</button> | ||
{loading ? 'Loading...' : repos.data.items.map(repo => ( | ||
<div key={repo.id}>{repo.name}</div> | ||
@@ -867,3 +821,2 @@ ))} | ||
| `onTimeout` | Called when the request times out. | empty function | | ||
| `path` | When using a global `url` set in the `Provider`, this is useful for adding onto it | `''` | | ||
| `persist` | Persists data for the duration of `cacheLife`. If `cacheLife` is not set it defaults to 24h. Currently only available in Browser. | `false` | | ||
@@ -877,3 +830,2 @@ | `perPage` | Stops making more requests if there is no more data to fetch. (i.e. if we have 25 todos, and the perPage is 10, after fetching 2 times, we will have 20 todos. The last 5 tells us we don't have any more to fetch because it's less than 10) For pagination. | `0` | | ||
| `timeout` | The request will be aborted/cancelled after this amount of time. This is also the interval at which `retries` will be made at. **in milliseconds**. If set to `0`, it will not timeout except for browser defaults. | `0` | | ||
| `url` | Allows you to set a base path so relative paths can be used for each request :) | empty string | | ||
@@ -922,5 +874,2 @@ ```jsx | ||
// if you have a global `url` set up, this is how you can add to it | ||
path: '/path/to/your/api', | ||
// this will tell useFetch not to run the request if the list doesn't haveMore. (pagination) | ||
@@ -974,5 +923,2 @@ // i.e. if the last page fetched was < 15, don't run the request again | ||
timeout: 10000, | ||
// used to be `baseUrl`. You can set your URL this way instead of as the 1st argument | ||
url: 'https://example.com', | ||
} | ||
@@ -1019,2 +965,5 @@ | ||
- [ ] prefetching | ||
- [ ] global cache state management | ||
- [ ] optimistic updates | ||
- [ ] `persist` support for React Native | ||
@@ -1021,0 +970,0 @@ - [ ] better loading state management. When using only 1 useFetch in a component and we use |
136455
1823
1057