Skip to content

Instantly share code, notes, and snippets.

@Faliszek
Last active August 13, 2020 14:16
Show Gist options
  • Save Faliszek/477f3c761767bb74665bbc98a37bc789 to your computer and use it in GitHub Desktop.
Save Faliszek/477f3c761767bb74665bbc98a37bc789 to your computer and use it in GitHub Desktop.
import React from 'react';
import { getToken } from '@igip-react/util-auth';
import { handleError, ApiError } from './utils/handleError';
import { getErrorMessage } from './utils/getErrorMessage';
export const HEADERS = {
Accept: 'application/json, text/javascript',
'Content-Type': 'application/json',
};
type Method = 'GET' | 'DELETE' | 'POST' | 'PUT';
export type Options<T> = {
method?: Method;
serialize?: (res: any) => T;
args?: RequestInit;
headers?: Record<string, string>;
body?: Object;
onSuccess?: (res: any) => void;
onError?: (res: ApiError) => void;
};
function parseParams(path: string, data?: Object) {
if (data) {
const arrayParams = Object.entries(data);
const params = new URLSearchParams(arrayParams);
const newUrl = data ? path + '?' + params.toString() : path;
return newUrl;
}
return path;
}
function parseBody(data?: any) {
return data ? { body: JSON.stringify(data) } : {};
}
const defaultOptions = {
onSuccess: () => {},
onError: () => {},
method: 'GET',
args: {},
serialize: undefined,
};
function createHeaders<T>(config?: Options<T>): Record<string, string> {
if (config && config.headers) {
return {
...HEADERS,
...config.headers,
};
}
return HEADERS;
}
export function useFetch<T>(
path: string,
initialData: T,
config?: Options<T>
): [(data?: any) => void, boolean, T, ApiError | null] {
const token = getToken();
const options = { ...defaultOptions, ...config };
const defaultHeaders = createHeaders(config);
const headers =
token === null
? defaultHeaders
: { ...defaultHeaders, 'X-SECURITY-TOKEN': token };
const { args, method, onSuccess, onError } = options;
const [data, setData] = React.useState(initialData);
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState<ApiError | null>(null);
const serialize = options.serialize ? options.serialize : (res: T) => res;
const parse = React.useCallback(serialize, []);
const params = React.useRef({ ...args, headers });
const onDone = React.useCallback(onSuccess, []);
const onFail = React.useCallback(onError, []);
const request = React.useCallback(
(data: Object) => {
const url = method === 'GET' ? parseParams(path, data) : path;
const body = method !== 'GET' ? parseBody(data) : {};
setLoading(true);
window
.fetch(url, { ...params.current, ...body, method })
.then((res) => {
if ([200, 201].includes(res.status)) {
return res.json().then((res) => {
const data = parse(res);
setData(data);
onDone(data);
});
}
return res.text().then((response) => {
return Promise.reject({
status: res.status,
message: getErrorMessage(response),
});
});
})
.catch((err: ApiError) => {
onFail(err);
setError(err);
})
.finally(() => setLoading(false));
},
[path, method, params, parse, onDone, onFail, args]
);
React.useEffect(() => {
if (error) {
handleError(error);
}
}, [error]);
return [request, loading, data, error];
}
export function useGet<T>(path: string, initialData: T, options?: Options<T>) {
return useFetch<T>(path, initialData, {
...options,
method: 'GET',
});
}
export function usePost<T>(path: string, initialData: T, options?: Options<T>) {
return useFetch<T>(path, initialData, {
...options,
method: 'POST',
});
}
export function usePut<T>(path: string, initialData: T, options?: Options<T>) {
return useFetch<T>(path, initialData, {
...options,
method: 'PUT',
});
}
export function useDelete<T>(path: string, initialData: T, options?: Options<T>) {
return useFetch<T>(path, initialData, {
...options,
method: 'DELETE',
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment