Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "http-react",
"version": "3.8.7",
"version": "3.8.8",
"description": "React hooks for data fetching",
"main": "dist/index.js",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions src/hooks/others.ts
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,7 @@ export function useServerAction<T extends (args: any) => any>(
let mockServerActionId = config?.id ?? getMockServerActionId(action)

const $action = useFetch(mockServerActionId, {
params: {},
fetcher: async function proxied(_, config) {
const actionParam = actionForms.get(mockServerActionId) ?? config?.params
$action.resetError()
Expand Down
17 changes: 13 additions & 4 deletions src/hooks/use-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import {
FetchContextType,
HTTP_METHODS,
ImperativeFetch,
StaticFetchConfig,
StaticFetchConfigNoUrl,
TimeSpan
} from '../types'

Expand Down Expand Up @@ -82,9 +84,16 @@ const temporaryFormData = new Map()
/**
* Fetch hook
*/
export function useFetch<FetchDataType = any, TransformData = any>(
init: FetchConfigType<FetchDataType, TransformData> | string | Request,
options?: FetchConfigTypeNoUrl<FetchDataType, TransformData>
export function useFetch<
FetchDataType = any,
TransformData = any,
UrlType extends string = string
>(
init:
| StaticFetchConfig<FetchDataType, TransformData, UrlType>
| UrlType
| Request,
options?: StaticFetchConfigNoUrl<FetchDataType, TransformData, UrlType>
) {
const $ctx = useHRFContext()

Expand Down Expand Up @@ -129,7 +138,7 @@ export function useFetch<FetchDataType = any, TransformData = any>(
...options,
// @ts-expect-error
id: init?.id ?? init?.key
} as Required<FetchConfigType<FetchDataType, TransformData>>)
} as Required<StaticFetchConfig<FetchDataType, TransformData, UrlType>>)

const {
onOnline = ctx.onOnline,
Expand Down
50 changes: 50 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,3 +371,53 @@ export type FetchInit<FDT = any, TransformData = any> = FetchConfigType<
FDT,
TransformData
>;

// types related to params parsing

/** Helper type to extract the parameter name from a segment like :id or {id} or [id] */
export type ExtractParam<Segment extends string> =
Segment extends `[${infer Name}]`
? Name // [name]
: Segment extends `:${infer Name}`
? Name // :name
: Segment extends `{${infer Name}}`
? Name // {name}
: never;

type CleanPath<Path extends string> = Path extends `/${infer Rest}`
? CleanPath<Rest>
: Path extends `${infer Rest}/`
? CleanPath<Rest>
: Path extends `${infer Base}?${any}`
? CleanPath<Base>
: Path;

type ParsePathParams<Path extends string> =
Path extends `${infer Segment}/${infer Rest}`
? (ExtractParam<Segment> extends never
? {}
: { [K in ExtractParam<Segment>]: string | number }) &
ParsePathParams<Rest>
: ExtractParam<Path> extends never
? {}
: { [K in ExtractParam<Path>]: string | number };

export type PathParams<Path extends string> = ParsePathParams<CleanPath<Path>>;

export type StaticParams<UrlType extends string> =
PathParams<UrlType> extends Record<string, never>
? { params?: any }
: { params: PathParams<UrlType> };

export type StaticFetchConfig<D, T, U extends string> = Omit<
FetchConfigType<D, T>,
"url" | "params"
> & {
url?: U;
} & StaticParams<U>;

export type StaticFetchConfigNoUrl<D, T, U extends string> = Omit<
FetchConfigTypeNoUrl<D, T>,
"params"
> &
StaticParams<U>;
65 changes: 36 additions & 29 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
requestInitialTimes,
requestsProvider,
runningMutate,
valuesMemory,
valuesMemory
} from "../internal";

import { UNITS_MILISECONDS_EQUIVALENTS } from "../internal/constants";
Expand All @@ -24,6 +24,8 @@ import {
TimeSpan,
FetchConfigTypeNoUrl,
FetchConfigType,
StaticFetchConfig,
StaticFetchConfigNoUrl
} from "../types";
import {
gql,
Expand All @@ -32,7 +34,7 @@ import {
isFunction,
queue,
serialize,
windowExists,
windowExists
} from "./shared";

export function getMiliseconds(v: TimeSpan): number {
Expand Down Expand Up @@ -67,28 +69,28 @@ export const createImperativeFetch = (ctx: FetchContextType) => {
"PATCH",
"PURGE",
"LINK",
"UNLINK",
"UNLINK"
];

const { baseUrl } = ctx;

return {
...Object.fromEntries(
new Map(
keys.map((k) => [
keys.map(k => [
k.toLowerCase(),
(url, config = {}) =>
(useFetch as any)[k.toLowerCase()](
hasBaseUrl(url) ? url : baseUrl + url,
{
...ctx,
...config,
...config
}
),
)
])
)
),
config: ctx,
config: ctx
} as ImperativeFetch;
};

Expand All @@ -101,7 +103,7 @@ export const useIsomorphicLayoutEffect = windowExists
*/
export function revalidate(id: any | any[], __reval__ = true) {
if (Array.isArray(id)) {
id.map((reqId) => {
id.map(reqId => {
if (isDefined(reqId)) {
const key = serialize(reqId);

Expand All @@ -118,7 +120,7 @@ export function revalidate(id: any | any[], __reval__ = true) {
queue(() => {
requestsProvider.emit(key, {
loading: true,
error: false,
error: false
});
});
}
Expand All @@ -142,7 +144,7 @@ export function revalidate(id: any | any[], __reval__ = true) {
queue(() => {
requestsProvider.emit(key, {
loading: true,
error: false,
error: false
});
});
}
Expand All @@ -157,17 +159,17 @@ export function revalidateKey(key: any) {

export function cancelRequest(id: any | any[]) {
if (Array.isArray(id)) {
id.map((reqId) => {
id.map(reqId => {
if (isDefined(reqId)) {
const key = serialize({
idString: serialize(reqId),
idString: serialize(reqId)
});
if (isPending(key)) {
revalidate(reqId, false);
queue(() => {
requestsProvider.emit(key, {
loading: false,
error: false,
error: false
});
});
}
Expand All @@ -176,14 +178,14 @@ export function cancelRequest(id: any | any[]) {
} else {
if (isDefined(id)) {
const key = serialize({
idString: serialize(id),
idString: serialize(id)
});
if (isPending(key)) {
revalidate(id, false);
queue(() => {
requestsProvider.emit(key, {
loading: false,
error: false,
error: false
});
});
}
Expand Down Expand Up @@ -249,7 +251,7 @@ export function queryProvider<R>(

const queryVariables = {
...thisDefaults?.variables,
...(otherConfig as any)?.variables,
...(otherConfig as any)?.variables
};

const { config = {} } = providerConfig || {};
Expand All @@ -276,7 +278,7 @@ export function queryProvider<R>(
headers: {
...others?.headers,
...thisDefaults?.headers,
...otherConfig?.headers,
...otherConfig?.headers
},
...{ __fromProvider: true },
default: {
Expand All @@ -287,15 +289,15 @@ export function queryProvider<R>(
* 'value' property (when using the `gql` function)
*/
// @ts-ignore
otherConfig?.default) as R[P]["value"],
otherConfig?.default) as R[P]["value"]
},
variables: queryVariables,
variables: queryVariables
});

const thisData = useMemo(
() => ({
...g?.data,
variables: queryVariables,
variables: queryVariables
}),
[serialize({ data: g?.data, queryVariables })]
);
Expand All @@ -304,9 +306,9 @@ export function queryProvider<R>(
...g,
config: {
...g?.config,
config: undefined,
config: undefined
},
data: thisData,
data: thisData
} as Omit<typeof g, "data"> & {
data: {
data: QuerysType[P] extends ReturnType<typeof gql>
Expand Down Expand Up @@ -338,7 +340,7 @@ export function mutateData(
requestsProvider.emit(key, {
data: newVal,
isMutating: true,
requestCallId,
requestCallId
});
if (_revalidate) {
previousConfig.set(key, undefined);
Expand All @@ -353,7 +355,7 @@ export function mutateData(
requestsProvider.emit(key, {
requestCallId,
isMutating: true,
data: v,
data: v
});
if (_revalidate) {
previousConfig.set(key, undefined);
Expand All @@ -368,11 +370,16 @@ export function mutateData(
}
}

export function fetchOptions<T = any, TransformData = any>(
init: string | FetchConfigType<T, TransformData>,
options?: FetchConfigTypeNoUrl<T, TransformData>
) {
export function fetchOptions<
T = any,
TransformData = any,
UrlType extends string = string
>(
init: UrlType | StaticFetchConfig<T, TransformData, UrlType>,
options?: Partial<StaticFetchConfigNoUrl<T, TransformData, UrlType>>
): StaticFetchConfig<T, TransformData, UrlType> {
// Changed return type
return (
typeof init === "string" ? { url: init, ...options } : init
) as FetchConfigType<T, TransformData>;
) as StaticFetchConfig<T, TransformData, UrlType>; // Changed cast
}
Loading