diff --git a/package.json b/package.json index 6fb3e6e..6860fe8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "http-react", - "version": "3.7.3", + "version": "3.7.4", "description": "React hooks for data fetching", "main": "dist/index.js", "scripts": { diff --git a/src/components/server/index.tsx b/src/components/server/index.tsx index 94c23a1..ed5242d 100644 --- a/src/components/server/index.tsx +++ b/src/components/server/index.tsx @@ -1,9 +1,9 @@ -"use client" +'use client' -import { FetchConfigAsync } from "../FetchConfigAsync" +import { FetchConfigAsync } from '../FetchConfigAsync' -import { FetchConfig as FConfig, SSRSuspense } from "../index" +import { FetchConfig as FConfig, SSRSuspense } from '../index' -const FetchConfig = typeof window === "undefined" ? FetchConfigAsync : FConfig +const FetchConfig = typeof window === 'undefined' ? FetchConfigAsync : FConfig export { FetchConfig, SSRSuspense, FConfig as FetchConfigSync } diff --git a/src/hooks/use-fetch.ts b/src/hooks/use-fetch.ts index 033c28e..cd84c02 100644 --- a/src/hooks/use-fetch.ts +++ b/src/hooks/use-fetch.ts @@ -1,5 +1,5 @@ -"use client" -import { useState, useEffect, useMemo, useRef, useCallback } from "react" +'use client' +import { useState, useEffect, useMemo, useRef, useCallback } from 'react' import { abortControllers, @@ -29,10 +29,10 @@ import { suspenseRevalidationStarted, onlineHandled, offlineHandled, - hasData, -} from "../internal" + hasData +} from '../internal' -import { DEFAULT_RESOLVER, METHODS } from "../internal/constants" +import { DEFAULT_RESOLVER, METHODS } from '../internal/constants' import { CustomResponse, @@ -41,16 +41,16 @@ import { FetchContextType, HTTP_METHODS, ImperativeFetch, - TimeSpan, -} from "../types" + TimeSpan +} from '../types' import { createImperativeFetch, getMiliseconds, getTimePassed, revalidate, - useIsomorphicLayoutEffect, -} from "../utils" + useIsomorphicLayoutEffect +} from '../utils' import { createRequestFn, getRequestHeaders, @@ -63,16 +63,16 @@ import { queue, serialize, setURLParams, - windowExists, -} from "../utils/shared" -import { $context } from "../internal/shared" + windowExists +} from '../utils/shared' +import { $context } from '../internal/shared' /** * Passing `undefined` to `new Date()` returns `Invalid Date {}`, so return null instead */ const getDateIfValid = (d: Date | null) => // @ts-ignore - Evals to a Date - (d?.toString() === "Invalid Date" || d === null ? null : d) as Date + (d?.toString() === 'Invalid Date' || d === null ? null : d) as Date /** * Termporary form data is set with the submit method in useFetch and is deleted immediately after resolving (see line #858) @@ -93,12 +93,12 @@ export function useFetch( ...$ctx, query: { ...$context?.value?.query, - ...$ctx?.query, + ...$ctx?.query }, headers: { ...$context.value?.headers, - ...$ctx?.headers, - }, + ...$ctx?.headers + } } const valueMap = new Map(Object.entries(ctx.value ?? {})) @@ -109,18 +109,18 @@ export function useFetch( const isRequest = init instanceof Object && init?.json const optionsConfig = - typeof init === "string" + typeof init === 'string' ? { // Pass init as the url if init is a string url: init, - ...options, + ...options } : isRequest ? { url: init.url, method: init.method, init, - ...options, + ...options } : (init as FetchConfigType) @@ -129,14 +129,14 @@ export function useFetch( onOffline = ctx.onOffline, onMutate, revalidateOnMount = ctx.revalidateOnMount, - url = "", + url = '', query = {}, params = {}, baseUrl = undefined, method = isRequest ? init.method : (METHODS.GET as HTTP_METHODS), headers = {} as Headers, body = undefined as unknown as Body, - formatBody = (e) => JSON.stringify(e), + formatBody = e => JSON.stringify(e), resolver = isFunction(ctx.resolver) ? ctx.resolver : DEFAULT_RESOLVER, onError, auto = isDefined(ctx.auto) ? ctx.auto : true, @@ -155,6 +155,7 @@ export function useFetch( maxCacheAge = ctx.maxCacheAge, fetcher = ctx.fetcher, middleware = ctx.middleware, + transform = ctx.transform } = optionsConfig const $fetch = isFunction(fetcher) @@ -170,7 +171,7 @@ export function useFetch( method, headers, body, - formatBody, + formatBody } const { cacheProvider: $cacheProvider = defaultCache } = ctx @@ -180,7 +181,7 @@ export function useFetch( const { cacheProvider = $cacheProvider } = optionsConfig - const requestCallId = useMemo(() => `${Math.random()}`.split(".")[1], []) + const requestCallId = useMemo(() => `${Math.random()}`.split('.')[1], []) const willResolve = isDefined(onResolve) const handleError = isDefined(onError) @@ -198,24 +199,24 @@ export function useFetch( const reqQuery = { ...ctx.query, - ...config.query, + ...config.query } const reqParams = { ...ctx.params, - ...config.params, + ...config.params } const rawUrl = (hasBaseUrl(url) - ? "" + ? '' : !isDefined(config.baseUrl) ? !isDefined(ctx.baseUrl) - ? "" + ? '' : ctx.baseUrl : config.baseUrl) + url - const defaultId = [method, url].join(" ") + const defaultId = [method, url].join(' ') const { id = defaultId } = optionsConfig @@ -234,22 +235,22 @@ export function useFetch( const resolvedDataKey = serialize({ idString, reqQuery, reqParams }) - const ageKey = ["max-age", resolvedDataKey].join("-") + const ageKey = ['max-age', resolvedDataKey].join('-') const paginationCache = cacheProvider.get(resolvedDataKey) const normalCache = cacheProvider.get(resolvedKey) - const maxAge = getMiliseconds(maxCacheAge || "0 ms") + const maxAge = getMiliseconds(maxCacheAge || '0 ms') // Revalidates if passed maxCacheAge has changed - if (!cacheProvider.get("maxAgeValue" + resolvedDataKey)) { - cacheProvider.set("maxAgeValue" + resolvedDataKey, maxCacheAge || "0 ms") + if (!cacheProvider.get('maxAgeValue' + resolvedDataKey)) { + cacheProvider.set('maxAgeValue' + resolvedDataKey, maxCacheAge || '0 ms') } else { - if (cacheProvider.get("maxAgeValue" + resolvedDataKey) !== maxCacheAge) { + if (cacheProvider.get('maxAgeValue' + resolvedDataKey) !== maxCacheAge) { cacheProvider.set(ageKey, 0) - cacheProvider.set("maxAgeValue" + resolvedDataKey, maxCacheAge) + cacheProvider.set('maxAgeValue' + resolvedDataKey, maxCacheAge) } } @@ -271,7 +272,7 @@ export function useFetch( const suspense = $suspense || willSuspend.get(resolvedKey) if (!suspense) { - if (url !== "") { + if (url !== '') { suspenseInitialized.set(resolvedKey, true) } } @@ -284,17 +285,17 @@ export function useFetch( const realUrl = urlWithParams + - (urlWithParams.includes("?") ? (optionsConfig?.query ? `&` : "") : "?") + (urlWithParams.includes('?') ? (optionsConfig?.query ? `&` : '') : '?') if (!previousProps.has(resolvedKey)) { - if (url !== "") { + if (url !== '') { previousProps.set(resolvedKey, optionsConfig) } } const configUrl = urls[resolvedKey] || { realUrl, - rawUrl, + rawUrl } const stringDeps = serialize( @@ -316,7 +317,7 @@ export function useFetch( useEffect(() => { if (isDefined(optionsConfig.default)) { if (!fetcherDefaults.has(resolvedKey)) { - if (url !== "") { + if (url !== '') { if (!isDefined(cacheProvider.get(resolvedDataKey))) { fetcherDefaults.set(resolvedKey, optionsConfig.default) } @@ -324,7 +325,7 @@ export function useFetch( if (!isDefined(cacheProvider.get(resolvedDataKey))) { requestsProvider.emit(resolvedKey, { requestCallId, - data: optionsConfig.default, + data: optionsConfig.default }) } } @@ -362,16 +363,16 @@ export function useFetch( ? isPending(resolvedKey) || (revalidateOnMount ? !jsonCompare( - JSON.parse(previousConfig.get(resolvedKey) || "{}"), + JSON.parse(previousConfig.get(resolvedKey) || '{}'), optionsConfig ) : !jsonCompare( - JSON.parse(previousConfig.get(resolvedKey) || "{}"), + JSON.parse(previousConfig.get(resolvedKey) || '{}'), optionsConfig )) : false, error: (hasErrors.get(resolvedDataKey) || false) as boolean, - completedAttempts: 0, + completedAttempts: 0 }) const thisDeps = useRef({ @@ -379,7 +380,7 @@ export function useFetch( online: false, loading: false, error: false, - completedAttempts: false, + completedAttempts: false }).current const inDeps = (k: keyof typeof thisDeps) => { @@ -400,20 +401,20 @@ export function useFetch( } function setData(v: any) { - setFetchState((p) => { + setFetchState(p => { if (isFunction(v)) { const newVal = v(p.data) if (!jsonCompare(p.data, newVal)) { return { ...p, - data: newVal, + data: newVal } } } else { if (!jsonCompare(p.data, v)) { return { ...p, - data: v, + data: v } } } @@ -426,11 +427,11 @@ export function useFetch( const rawJSON = serialize(data) function setOnline(v: any) { - setFetchState((p) => { + setFetchState(p => { if (online !== p.online) { return { ...p, - online: v, + online: v } } return p @@ -439,24 +440,24 @@ export function useFetch( const requestHeaders = { ...ctx.headers, - ...config.headers, + ...config.headers } function setError(v: any) { - setFetchState((p) => { + setFetchState(p => { if (isFunction(v)) { const newErroValue = v(p.error) if (newErroValue !== p.error) { return { ...p, - error: newErroValue, + error: newErroValue } } } else { if (v !== p.error) { return { ...p, - error: v, + error: v } } } @@ -465,20 +466,20 @@ export function useFetch( } function setLoading(v: any) { - setFetchState((p) => { + setFetchState(p => { if (isFunction(v)) { const newLoadingValue = v(p.loading) if (newLoadingValue !== p.loading) { return { ...p, - loading: newLoadingValue, + loading: newLoadingValue } } } else { if (v !== p.loading) { return { ...p, - loading: v, + loading: v } } } @@ -487,20 +488,20 @@ export function useFetch( } function setCompletedAttempts(v: any) { - setFetchState((p) => { + setFetchState(p => { if (isFunction(v)) { const newCompletedAttempts = v(p.completedAttempts) if (newCompletedAttempts !== p.completedAttempts) { return { ...p, - completedAttempts: newCompletedAttempts, + completedAttempts: newCompletedAttempts } } } else { if (v !== p.completedAttempts) { return { ...p, - completedAttempts: v, + completedAttempts: v } } } @@ -511,7 +512,7 @@ export function useFetch( const requestAbortController: AbortController = abortControllers.get(resolvedKey) ?? new AbortController() - const isGqlRequest = isDefined((optionsConfig as any)["__gql"]) + const isGqlRequest = isDefined((optionsConfig as any)['__gql']) const fetchData = useCallback( async function fetchData( @@ -519,10 +520,10 @@ export function useFetch( ) { const rawUrl = (hasBaseUrl(url) - ? "" + ? '' : !isDefined(config.baseUrl) ? !isDefined(ctx.baseUrl) - ? "" + ? '' : ctx.baseUrl : config.baseUrl) + url @@ -530,20 +531,20 @@ export function useFetch( const realUrl = urlWithParams + - (urlWithParams.includes("?") ? (c?.query !== "" ? `&` : "") : "") + (urlWithParams.includes('?') ? (c?.query !== '' ? `&` : '') : '') if ( !jsonCompare( - JSON.parse(previousConfig.get(resolvedKey) || "{}"), + JSON.parse(previousConfig.get(resolvedKey) || '{}'), optionsConfig ) ) { previousProps.set(resolvedKey, optionsConfig) queue(() => { - if (url !== "") { + if (url !== '') { const newUrls = { realUrl, - rawUrl, + rawUrl } urls[resolvedKey] = newUrls @@ -567,7 +568,7 @@ export function useFetch( requestCallId: loadingFirst ? requestCallId : undefined, loading: true, requestAbortController: newAbortController, - error: false, + error: false }) abortControllers.set(resolvedKey, newAbortController) @@ -589,7 +590,7 @@ export function useFetch( } } - cacheProvider.set("requestStart" + resolvedDataKey, Date.now()) + cacheProvider.set('requestStart' + resolvedDataKey, Date.now()) requestInitialTimes.set(resolvedDataKey, Date.now()) const newRequestConfig = ( @@ -602,12 +603,12 @@ export function useFetch( return newAbortController.signal })(), headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', ...ctx.headers, ..._headers, ...config.headers, - ...c.headers, - }, + ...c.headers + } } : { ...ctx, @@ -624,23 +625,23 @@ export function useFetch( headers: { ...(temporaryFormData.get(resolvedKey) ? {} - : { "Content-Type": "application/json" }), + : { 'Content-Type': 'application/json' }), ...ctx.headers, ...config.headers, - ...c.headers, - }, + ...c.headers + } } ) as any const r = new Request( ( realUrl + - (realUrl.includes("?") + (realUrl.includes('?') ? c.query : c.query - ? "?" + c.query + ? '?' + c.query : c.query) - ).replace("?&", "?"), + ).replace('?&', '?'), newRequestConfig ) @@ -654,11 +655,11 @@ export function useFetch( const resolvedDate = Date.now() cacheProvider.set( - "expiration" + resolvedDataKey, + 'expiration' + resolvedDataKey, resolvedDate + maxAge ) - cacheProvider.set("requestEnds" + resolvedDataKey, resolvedDate) + cacheProvider.set('requestEnds' + resolvedDataKey, resolvedDate) requestResponseTimes.set( resolvedDataKey, getTimePassed(resolvedDataKey) @@ -673,14 +674,14 @@ export function useFetch( ...rpc, response: json, error: false, - code, + code } // @ts-ignore - 'data' is priority because 'fetcher' can return it - const incoming = json?.["data"] ?? (await (resolver as any)(json)) + const incoming = json?.['data'] ?? (await (resolver as any)(json)) - // @ts-expect-error - const actionError = json?.["error"] + // @ts-ignore + const actionError = json?.['error'] const _data = isFunction(middleware) ? await middleware!(incoming as any, thisCache) @@ -690,7 +691,7 @@ export function useFetch( ? { ..._data, variables: (optionsConfig as any)?.variables, - errors: _data?.errors ? _data.errors : undefined, + errors: _data?.errors ? _data.errors : undefined } : _data @@ -708,7 +709,7 @@ export function useFetch( rpc = { ...rpc, - error: false, + error: false } const dataExpirationTime = Date.now() + maxAge @@ -719,7 +720,7 @@ export function useFetch( hasErrors.set(resolvedKey, true) rpc = { ...rpc, - error: true, + error: true } if (handleError) { if (!resolvedOnErrorCalls.get(resolvedKey)) { @@ -737,7 +738,7 @@ export function useFetch( variables: isGqlRequest ? (optionsConfig as any)?.variables || {} : undefined, - completedAttempts: 0, + completedAttempts: 0 } $$data = __data @@ -746,7 +747,7 @@ export function useFetch( if (!_data?.errors && isGqlRequest) { rpc = { ...rpc, - error: false, + error: false } hasErrors.set(resolvedDataKey, false) @@ -769,7 +770,7 @@ export function useFetch( } else { rpc = { ...rpc, - error: actionError ?? true, + error: actionError ?? true } if (!cacheIfError) { hasData.set(resolvedDataKey, false) @@ -780,11 +781,11 @@ export function useFetch( hasData.set(resolvedDataKey, false) hasData.set(resolvedKey, false) } - setFetchState((previous) => { + setFetchState(previous => { const newData = { ...previous, variables: (optionsConfig as any)?.variables, - errors: _data.errors, + errors: _data.errors } as any $$data = newData @@ -794,7 +795,7 @@ export function useFetch( rpc = { ...rpc, data: newData, - error: actionError ?? true, + error: actionError ?? true } cacheProvider.set(resolvedDataKey, newData) @@ -815,7 +816,7 @@ export function useFetch( rpc = { ...rpc, - data: def, + data: def } } if (handleError) { @@ -849,7 +850,7 @@ export function useFetch( rpc = { ...rpc, - error: err ?? true, + error: err ?? true } if (cacheIfError) { @@ -859,7 +860,7 @@ export function useFetch( rpc = { ...rpc, - data: thisCache, + data: thisCache } } } else { @@ -867,7 +868,7 @@ export function useFetch( rpc = { ...rpc, - data: def, + data: def } cacheForMutation.set(idString, def) @@ -875,7 +876,7 @@ export function useFetch( rpc = { ...rpc, - error: err ?? true, + error: err ?? true } hasErrors.set(resolvedDataKey, err ?? true) @@ -889,7 +890,7 @@ export function useFetch( } else { rpc = { ...rpc, - loading: true, + loading: true } if (!isPending(resolvedKey)) { if (!isDefined(cacheProvider.get(resolvedDataKey))) { @@ -900,7 +901,7 @@ export function useFetch( rpc = { ...rpc, data: def, - loading: true, + loading: true } } } @@ -919,7 +920,7 @@ export function useFetch( hasErrors.get(resolvedDataKey) || false, ...rpc, - loading: false, + loading: false }) willSuspend.set(resolvedKey, false) @@ -944,7 +945,7 @@ export function useFetch( requestCallId, memory, def, - loadingFirst, + loadingFirst ] ) @@ -960,21 +961,21 @@ export function useFetch( } } } - signal?.addEventListener("abort", abortCallback) + signal?.addEventListener('abort', abortCallback) return () => { - signal?.removeEventListener("abort", abortCallback) + signal?.removeEventListener('abort', abortCallback) } }, [requestAbortController, resolvedKey, onAbort, loading]) const imperativeFetch = useMemo(() => { const __headers = { ...ctx.headers, - ...config.headers, + ...config.headers } const __params = { ...ctx.params, - ...config.params, + ...config.params } const __baseUrl = isDefined(config.baseUrl) ? config.baseUrl : ctx.baseUrl @@ -982,7 +983,7 @@ export function useFetch( ...ctx, headers: __headers, baseUrl: __baseUrl, - params: __params, + params: __params }) }, [serialize(ctx)]) @@ -994,7 +995,7 @@ export function useFetch( error: $error, online, loading, - completedAttempts, + completedAttempts } = v || {} if (isMutating) { @@ -1003,7 +1004,7 @@ export function useFetch( if (isMutating) { forceMutate($data) if (handleMutate) { - if (url === "") { + if (url === '') { ;(onMutate as any)($data, imperativeFetch) } else { if (!runningMutate.get(resolvedKey)) { @@ -1019,31 +1020,31 @@ export function useFetch( if (v.requestCallId !== requestCallId) { if (!willSuspend.get(resolvedKey)) { queue(() => { - if (inDeps("data")) { + if (inDeps('data')) { if (isDefined($data)) { if (!jsonCompare(data, cacheProvider.get(resolvedDataKey))) { setData(cacheProvider.get(resolvedKey)) } } } - if (inDeps("online")) { + if (inDeps('online')) { if (isDefined(online)) { setOnline(online) } } - if (inDeps("loading")) { + if (inDeps('loading')) { if (isDefined(loading)) { setLoading(loading) } } - if (inDeps("error")) { + if (inDeps('error')) { if (isDefined($error)) { if (fetchState.error !== $error) { setError($error) } } } - if (inDeps("completedAttempts")) { + if (inDeps('completedAttempts')) { if (isDefined(completedAttempts)) { setCompletedAttempts(completedAttempts) } @@ -1064,7 +1065,7 @@ export function useFetch( resolvedKey, resolvedDataKey, requestCallId, - fetchState, + fetchState ]) const reValidate = useCallback( @@ -1081,18 +1082,18 @@ export function useFetch( if (!isPending(resolvedKey)) { // preventing revalidation where only need updates about // 'loading', 'error' and 'data' because the url can be ommited. - if (url !== "") { + if (url !== '') { fetchData({ query: Object.keys(reqQuery) - .map((q) => + .map(q => Array.isArray(reqQuery[q]) ? reqQuery[q] - .map((queryItem: any) => [q, queryItem].join("=")) - .join("&") - : [q, reqQuery[q]].join("=") + .map((queryItem: any) => [q, queryItem].join('=')) + .join('&') + : [q, reqQuery[q]].join('=') ) - .join("&"), - params: reqParams, + .join('&'), + params: reqParams }) } } @@ -1113,7 +1114,7 @@ export function useFetch( ctx.auto, idString, fetchState, - id, + id ]) useEffect(() => { @@ -1124,7 +1125,7 @@ export function useFetch( } requestsProvider.emit(resolvedKey, { requestCallId, - online: true, + online: true }) setOnline(true) offlineHandled.set(resolvedKey, false) @@ -1141,9 +1142,9 @@ export function useFetch( function addOnlineListener() { if (windowExists) { - if ("addEventListener" in window) { + if ('addEventListener' in window) { if (retryOnReconnect) { - window.addEventListener("online", backOnline) + window.addEventListener('online', backOnline) } } } @@ -1153,8 +1154,8 @@ export function useFetch( return () => { if (windowExists) { - if ("addEventListener" in window) { - window.removeEventListener("online", backOnline) + if ('addEventListener' in window) { + window.removeEventListener('online', backOnline) } } } @@ -1166,7 +1167,7 @@ export function useFetch( setOnline(false) requestsProvider.emit(resolvedKey, { requestCallId, - online: false, + online: false }) onlineHandled.set(resolvedKey, false) if (handleOffline) { @@ -1179,8 +1180,8 @@ export function useFetch( function addOfflineListener() { if (windowExists) { - if ("addEventListener" in window) { - window.addEventListener("offline", wentOffline) + if ('addEventListener' in window) { + window.addEventListener('offline', wentOffline) } } } @@ -1189,8 +1190,8 @@ export function useFetch( return () => { if (windowExists) { - if ("addEventListener" in window) { - window.removeEventListener("offline", wentOffline) + if ('addEventListener' in window) { + window.removeEventListener('offline', wentOffline) } } } @@ -1200,7 +1201,7 @@ export function useFetch( return () => { if (revalidateOnMount) { if (canRevalidate) { - if (url !== "") { + if (url !== '') { if (suspenseInitialized.get(resolvedKey)) { queue(() => { previousConfig.set(resolvedKey, undefined) @@ -1233,7 +1234,7 @@ export function useFetch( if (!gettingAttempts.get(resolvedKey)) { gettingAttempts.set(resolvedKey, true) const attempts = - typeof $attempts === "function" + typeof $attempts === 'function' ? $attempts({ status: statusCodes.get(resolvedKey) || @@ -1243,7 +1244,7 @@ export function useFetch( hasErrors.get(resolvedKey) || hasErrors.get(resolvedDataKey) || (error as any), - completedAttempts, + completedAttempts }) : $attempts @@ -1255,7 +1256,7 @@ export function useFetch( requestsProvider.emit(resolvedKey, { requestCallId, - completedAttempts: newAttemptsValue, + completedAttempts: newAttemptsValue }) return newAttemptsValue @@ -1264,9 +1265,9 @@ export function useFetch( requestsProvider.emit(resolvedKey, { requestCallId, online: false, - error: true, + error: true }) - if (inDeps("online")) setOnline(false) + if (inDeps('online')) setOnline(false) } } } @@ -1284,7 +1285,7 @@ export function useFetch( fetchState, attemptInterval, resolvedKey, - completedAttempts, + completedAttempts ]) useEffect(() => { @@ -1307,7 +1308,7 @@ export function useFetch( rawJSON, canRevalidate, completedAttempts, - config, + config ]) const initializeRevalidation = useCallback( @@ -1315,28 +1316,28 @@ export function useFetch( ? async function initializeRevalidation() { let d = undefined if (canRevalidate) { - if (url !== "") { + if (url !== '') { d = await fetchData({ query: Object.keys(reqQuery) - .map((q) => + .map(q => Array.isArray(reqQuery[q]) ? reqQuery[q] - .map((queryItem: any) => [q, queryItem].join("=")) - .join("&") - : [q, reqQuery[q]].join("=") + .map((queryItem: any) => [q, queryItem].join('=')) + .join('&') + : [q, reqQuery[q]].join('=') ) - .join("&"), - params: reqParams, + .join('&'), + params: reqParams }) } else { d = def // It means a url is not passed - setFetchState((prev) => ({ + setFetchState(prev => ({ ...prev, loading: false, error: hasErrors.get(resolvedDataKey) || hasErrors.get(resolvedKey), - completedAttempts: prev.completedAttempts, + completedAttempts: prev.completedAttempts })) } } else { @@ -1353,17 +1354,17 @@ export function useFetch( ) if (!suspense) { - if (url !== "") { + if (url !== '') { suspenseInitialized.set(resolvedKey, true) } } useIsomorphicLayoutEffect(() => { const fn = () => { - if (url !== "") { + if (url !== '') { if (!jsonCompare(previousProps.get(resolvedKey), optionsConfig)) { abortControllers.get(resolvedKey)?.abort() - if (inDeps("data")) { + if (inDeps('data')) { queue(initializeRevalidation) } } @@ -1412,15 +1413,15 @@ Learn more: https://httpr.vercel.app/docs/api#suspense const fn = () => { if (!runningRequests.get(resolvedKey) && canRevalidate) { if (windowExists) { - if (canRevalidate && url !== "") { + if (canRevalidate && url !== '') { if ( !jsonCompare( - JSON.parse(previousConfig.get(resolvedKey) || "{}"), + JSON.parse(previousConfig.get(resolvedKey) || '{}'), optionsConfig ) ) { if (!isPending(resolvedKey)) { - if (inDeps("data")) { + if (inDeps('data')) { initializeRevalidation() } } else { @@ -1445,13 +1446,13 @@ Learn more: https://httpr.vercel.app/docs/api#suspense const revalidateAfterUnmount = revalidateOnMount ? true : !jsonCompare( - JSON.parse(previousConfig.get(resolvedKey) || "{}"), + JSON.parse(previousConfig.get(resolvedKey) || '{}'), optionsConfig ) function revalidate() { if (!debounce && !canDebounce.get(resolvedKey)) { - if (inDeps("data")) { + if (inDeps('data')) { initializeRevalidation() } } @@ -1483,8 +1484,8 @@ Learn more: https://httpr.vercel.app/docs/api#suspense useEffect(() => { function addFocusListener() { if (revalidateOnFocus && windowExists) { - if ("addEventListener" in window) { - window.addEventListener("focus", reValidate as any) + if ('addEventListener' in window) { + window.addEventListener('focus', reValidate as any) } } } @@ -1493,8 +1494,8 @@ Learn more: https://httpr.vercel.app/docs/api#suspense return () => { if (windowExists) { - if ("addEventListener" in window) { - window.removeEventListener("focus", reValidate as any) + if ('addEventListener' in window) { + window.removeEventListener('focus', reValidate as any) } } } @@ -1506,7 +1507,7 @@ Learn more: https://httpr.vercel.app/docs/api#suspense loading, reValidate, refresh, - serialize(config), + serialize(config) ]) const __config = { @@ -1515,20 +1516,20 @@ Learn more: https://httpr.vercel.app/docs/api#suspense ...previousProps.get(resolvedKey), params: { ...reqParams, - ...previousProps.get(resolvedKey)?.params, + ...previousProps.get(resolvedKey)?.params }, headers: { ...requestHeaders, - ...previousProps.get(resolvedKey)?.headers, + ...previousProps.get(resolvedKey)?.headers }, body: config.body, baseUrl: ctx.baseUrl || config.baseUrl, - url: configUrl?.realUrl?.replace("?", ""), + url: configUrl?.realUrl?.replace('?', ''), rawUrl: configUrl?.rawUrl, query: { ...reqQuery, - ...previousProps.get(resolvedKey)?.query, - }, + ...previousProps.get(resolvedKey)?.query + } } function forceMutate( @@ -1548,7 +1549,7 @@ Learn more: https://httpr.vercel.app/docs/api#suspense requestsProvider.emit(resolvedKey, { requestCallId, isMutating: true, - data: newValue, + data: newValue }) setData(newValue as any) } @@ -1564,7 +1565,7 @@ Learn more: https://httpr.vercel.app/docs/api#suspense requestsProvider.emit(resolvedKey, { requestCallId, isMutating: true, - data: newVal, + data: newVal }) setData(newVal) @@ -1573,12 +1574,12 @@ Learn more: https://httpr.vercel.app/docs/api#suspense } const [$requestStart, $requestEnd] = [ - notNull(cacheProvider.get("requestStart" + resolvedDataKey)) - ? new Date(cacheProvider.get("requestStart" + resolvedDataKey)) - : null, - notNull(cacheProvider.get("requestEnds" + resolvedDataKey)) - ? new Date(cacheProvider.get("requestEnds" + resolvedDataKey)) + notNull(cacheProvider.get('requestStart' + resolvedDataKey)) + ? new Date(cacheProvider.get('requestStart' + resolvedDataKey)) : null, + notNull(cacheProvider.get('requestEnds' + resolvedDataKey)) + ? new Date(cacheProvider.get('requestEnds' + resolvedDataKey)) + : null ] const expirationDate = error @@ -1587,16 +1588,20 @@ Learn more: https://httpr.vercel.app/docs/api#suspense : null : maxAge === 0 ? null - : notNull(cacheProvider.get("expiration" + resolvedDataKey)) - ? new Date(cacheProvider.get("expiration" + resolvedDataKey)) + : notNull(cacheProvider.get('expiration' + resolvedDataKey)) + ? new Date(cacheProvider.get('expiration' + resolvedDataKey)) : null const isFailed = hasErrors.get(resolvedDataKey) || hasErrors.get(resolvedKey) || error - const responseData = + const dataCandidate = (error && isFailed ? (cacheIfError ? thisCache : null) : thisCache) ?? def + const responseData = isDefined(dataCandidate) + ? transform!(dataCandidate) + : dataCandidate + const isSuccess = !isLoading && !isFailed const oneRequestResolved = @@ -1610,7 +1615,7 @@ Learn more: https://httpr.vercel.app/docs/api#suspense if (isFormData(form)) { if (formRef.current) { if (onSubmit) { - if (onSubmit !== "reset") { + if (onSubmit !== 'reset') { onSubmit(formRef.current, form) } else { if (formRef.current) { @@ -1634,7 +1639,7 @@ Learn more: https://httpr.vercel.app/docs/api#suspense hasErrors.set(resolvedDataKey, false) requestsProvider.emit(resolvedKey, { - error: false, + error: false }) } @@ -1649,7 +1654,7 @@ Learn more: https://httpr.vercel.app/docs/api#suspense }, formProps: { action: submit, - ref: formRef, + ref: formRef }, formRef, get revalidating() { @@ -1746,7 +1751,7 @@ Learn more: https://httpr.vercel.app/docs/api#suspense requestCallId, error: false, loading: false, - data: requestCache, + data: requestCache }) } } @@ -1760,7 +1765,7 @@ Learn more: https://httpr.vercel.app/docs/api#suspense /** * The request key */ - key: resolvedKey, + key: resolvedKey } as unknown as { refresh(): void resetError(): void @@ -1808,15 +1813,15 @@ Learn more: https://httpr.vercel.app/docs/api#suspense } } -useFetch.get = createRequestFn("GET", "", {}) -useFetch.delete = createRequestFn("DELETE", "", {}) -useFetch.head = createRequestFn("HEAD", "", {}) -useFetch.options = createRequestFn("OPTIONS", "", {}) -useFetch.post = createRequestFn("POST", "", {}) -useFetch.put = createRequestFn("PUT", "", {}) -useFetch.patch = createRequestFn("PATCH", "", {}) -useFetch.purge = createRequestFn("PURGE", "", {}) -useFetch.link = createRequestFn("LINK", "", {}) -useFetch.unlink = createRequestFn("UNLINK", "", {}) +useFetch.get = createRequestFn('GET', '', {}) +useFetch.delete = createRequestFn('DELETE', '', {}) +useFetch.head = createRequestFn('HEAD', '', {}) +useFetch.options = createRequestFn('OPTIONS', '', {}) +useFetch.post = createRequestFn('POST', '', {}) +useFetch.put = createRequestFn('PUT', '', {}) +useFetch.patch = createRequestFn('PATCH', '', {}) +useFetch.purge = createRequestFn('PURGE', '', {}) +useFetch.link = createRequestFn('LINK', '', {}) +useFetch.unlink = createRequestFn('UNLINK', '', {}) useFetch.extend = createImperativeFetch diff --git a/src/internal/constants.ts b/src/internal/constants.ts index 5800cba..ef003dd 100644 --- a/src/internal/constants.ts +++ b/src/internal/constants.ts @@ -9,21 +9,22 @@ const ATTEMPT_INTERVAL = 2 const REVALIDATE_ON_FOCUS = false const RETRY_ON_RECONNECT = true const REVALIDATE_ON_MOUNT = true -const DEFAULT_GRAPHQL_PATH = '/graphql' +const DEFAULT_GRAPHQL_PATH = "/graphql" const DEFAULT_RESOLVER = (e: any) => e.json() const DEFAULT_MIDDLEWARE = (incoming: any, previous: any) => incoming +const DEFAULT_TRANSFORM = (fetchData: any) => fetchData const METHODS = { - GET: 'GET', - DELETE: 'DELETE', - HEAD: 'HEAD', - OPTIONS: 'OPTIONS', - POST: 'POST', - PUT: 'PUT', - PATCH: 'PATCH', - PURGE: 'PURGE', - LINK: 'LINK', - UNLINK: 'UNLINK' + GET: "GET", + DELETE: "DELETE", + HEAD: "HEAD", + OPTIONS: "OPTIONS", + POST: "POST", + PUT: "PUT", + PATCH: "PATCH", + PURGE: "PURGE", + LINK: "LINK", + UNLINK: "UNLINK", } const UNITS_MILISECONDS_EQUIVALENTS = { @@ -34,7 +35,7 @@ const UNITS_MILISECONDS_EQUIVALENTS = { d: 86400000, we: 604800000, mo: 2629800000, - y: 31536000000 + y: 31536000000, } export { @@ -53,5 +54,6 @@ export { DEFAULT_RESOLVER, METHODS, UNITS_MILISECONDS_EQUIVALENTS, - DEFAULT_MIDDLEWARE + DEFAULT_MIDDLEWARE, + DEFAULT_TRANSFORM, } diff --git a/src/internal/index.ts b/src/internal/index.ts index 9109aba..29b3487 100644 --- a/src/internal/index.ts +++ b/src/internal/index.ts @@ -1,12 +1,13 @@ -'use client' -import { createContext, useContext } from 'react' +"use client" +import { createContext, useContext } from "react" -import { CacheStoreType, FetchContextType } from '../types' +import { CacheStoreType, FetchContextType } from "../types" import { ATTEMPTS, ATTEMPT_INTERVAL, DEFAULTS, DEFAULT_MIDDLEWARE, + DEFAULT_TRANSFORM, ONLINE, ON_OFFLINE, ON_ONLINE, @@ -14,9 +15,9 @@ import { QUERY, RETRY_ON_RECONNECT, REVALIDATE_ON_FOCUS, - REVALIDATE_ON_MOUNT -} from './constants' -import { $context } from './shared' + REVALIDATE_ON_MOUNT, +} from "./constants" +import { $context } from "./shared" /** * This marks which requests are running @@ -134,7 +135,7 @@ export const defaultCache: CacheStoreType = { }, remove(k) { resolvedRequests.delete(k) - } + }, } const requestsSubscribers = new Map() @@ -162,7 +163,7 @@ export const requestsProvider = { listener(payload) }) } - } + }, } const defaultContextVaue: FetchContextType = { @@ -179,7 +180,8 @@ const defaultContextVaue: FetchContextType = { revalidateOnMount: REVALIDATE_ON_MOUNT, cacheIfError: true, middleware: DEFAULT_MIDDLEWARE, - ...$context.value + transform: DEFAULT_TRANSFORM, + ...$context.value, } export const FetchContext = createContext(defaultContextVaue) diff --git a/src/types/index.ts b/src/types/index.ts index 92a68d1..0d3af63 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,14 +1,14 @@ export type HTTP_METHODS = - | 'GET' - | 'DELETE' - | 'HEAD' - | 'OPTIONS' - | 'POST' - | 'PUT' - | 'PATCH' - | 'PURGE' - | 'LINK' - | 'UNLINK' + | "GET" + | "DELETE" + | "HEAD" + | "OPTIONS" + | "POST" + | "PUT" + | "PATCH" + | "PURGE" + | "LINK" + | "UNLINK" export type FetchContextType = { clientOnly?: boolean @@ -55,6 +55,7 @@ export type FetchContextType = { suspense?: any[] resolver?: (r: Response) => any middleware?(incomindgData: any, previousData: any): any + transform?(fetchData: any): any children?: any auto?: boolean memory?: boolean @@ -82,7 +83,7 @@ export type FetchContextType = { ctx: FetchContextType ): void maxCacheAge?: TimeSpan -} & Omit +} & Omit export type CacheStoreType = { get(k?: any): any @@ -90,7 +91,7 @@ export type CacheStoreType = { remove?(k?: any): any } -export type CustomResponse = Omit & { +export type CustomResponse = Omit & { json(): Promise } @@ -102,7 +103,7 @@ export type RequestWithBody = ( /** * The request configuration */ - reqConfig?: Omit, 'suspense'> & { + reqConfig?: Omit, "suspense"> & { /** * Default value */ @@ -143,7 +144,7 @@ export type RequestWithBody = ( export type TimeSpan = | number - | `${string} ${'ms' | 'sec' | 'min' | 'h' | 'd' | 'we' | 'mo' | 'y'}` + | `${string} ${"ms" | "sec" | "min" | "h" | "d" | "we" | "mo" | "y"}` /** * An imperative version of the `useFetch` hook @@ -164,7 +165,7 @@ export type ImperativeFetch = { export type FetchConfigType = Omit< RequestInit, - 'body' | 'headers' + "body" | "headers" > & { headers?: any /** @@ -176,6 +177,7 @@ export type FetchConfigType = Omit< incomindgData: FetchDataType, previousData: FetchDataType ): FetchDataType + transform?(fetchData: FetchDataType): FetchDataType fetcher?( url: string, config: FetchConfigType @@ -217,7 +219,7 @@ export type FetchConfigType = Omit< * @default true */ memory?: boolean - onSubmit?: 'reset' | ((form: HTMLFormElement, data: FormData) => void) + onSubmit?: "reset" | ((form: HTMLFormElement, data: FormData) => void) /** * Function to run when request is resolved succesfuly */ @@ -335,11 +337,11 @@ export type FetchConfigType = Omit< /** * Will run when the request is sent */ - onFetchStart?: FetchContextType['onFetchStart'] + onFetchStart?: FetchContextType["onFetchStart"] /** * Will run when the response is received */ - onFetchEnd?: FetchContextType['onFetchEnd'] + onFetchEnd?: FetchContextType["onFetchEnd"] /** * If `true`, the last resolved value be returned as `data` if the request fails. If `false`, the default value will be returned instead * @@ -355,7 +357,7 @@ export type FetchConfigType = Omit< // If first argument is a string export type FetchConfigTypeNoUrl = Omit< FetchConfigType, - 'url' + "url" > /**