This repository has been archived on 2023-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
react-rest-request/src/request-hook.ts

117 lines
3.8 KiB
TypeScript

import React from 'react';
import invariant from 'tiny-invariant';
import isEqual from 'lodash.isequal';
import { useClient } from './client-hook';
import { Endpoint, Method } from './endpoint';
import { RequestAction, requestReducer, RequestState } from './reducer';
import { useRequestContext } from './request-context';
export type RequestConfig<R, V, P = void> = Readonly<{
variables?: V;
params?: P;
headers?: Record<string, string>;
onComplete?: (data: R) => unknown;
}>
export type RequestHandlerConfig<R, V, P> = Readonly<
RequestConfig<R, V, P>
& { force?: boolean }
>
export type RequestHandler<R, V, P> = (config?: RequestHandlerConfig<R, V, P>) => Promise<R | null>;
export function useRequest<R = Record<string, any>, V = Record<string, any>, P = void>(
endpoint: Endpoint<P>,
config?: RequestConfig<R, V, P>,
): [RequestHandler<R, V, P>, RequestState<R>] {
const [client] = useClient();
const { defaultHeaders } = useRequestContext();
const [state, dispatch] = React.useReducer<React.Reducer<RequestState<R>, RequestAction<R>>>(
requestReducer,
{
data: null,
loading: false,
isCalled: false,
}
);
const variableParam = requestVariableParamByMethod(endpoint.method);
const handler = React.useCallback(
(handlerConfig?: RequestHandlerConfig<R, V, P>) => {
if (state?.loading) {
return Promise.resolve(null);
}
let params: P | undefined;
let endpointUrl: string;
let isSameRequest = true;
if (typeof endpoint.url === 'function') {
params = handlerConfig?.params ?? config?.params;
invariant(params, 'Endpoind required params');
endpointUrl = endpoint.url(params);
isSameRequest = !!state?.prevParams && isEqual(state.prevParams, params);
} else {
endpointUrl = endpoint.url;
}
const variables = {
...config?.variables,
...handlerConfig?.variables,
};
const headers = {
...defaultHeaders,
...endpoint.headers,
...config?.headers,
...handlerConfig?.headers,
};
if (
isSameRequest
&& state?.prevVariables && isEqual(state.prevVariables, variables)
&& state?.prevHeaders && isEqual(state.prevHeaders, headers)
&& !handlerConfig?.force
) {
return Promise.resolve(state.data);
}
const onComplete = config?.onComplete ?? handlerConfig?.onComplete;
dispatch({ type: 'call', headers, variables, params });
return client
.request<R>({
...endpoint,
url: endpointUrl,
headers,
[variableParam]: variables,
})
.then(
(response) => {
dispatch({ type: 'success', response });
if (typeof onComplete === 'function') {
onComplete(response.data);
}
return response.data;
},
(response) => {
dispatch({ type: 'failure', response });
throw response;
}
);
},
[state, config, client, endpoint, variableParam, defaultHeaders]
);
return [
handler,
state,
];
}
function requestVariableParamByMethod(method: Method) {
return [Method.GET, Method.DELETE].includes(method) ? 'params' : 'data';
}