The aim is to create a reusable react hook to make api requests using axios and it should take all the axios request configs. It should also tell the status of the request: isLoading
or error
.
// workers/useApiWorker.ts
import React, { useCallback, useRef } from 'react';
import { AxiosRequestConfig, AxiosResponseTransformer } from 'axios';
import axios from '../../_axios';
interface Props {
enabled: boolean;
url: string;
method: AxiosRequestConfig['method'];
transformResponse?: AxiosResponseTransformer;
}
interface UseApiWorker<T> {
data: T | null;
isLoading: boolean;
error: any | Error | Record<string, any>;
refetch: (requestConfig: AxiosRequestConfig) => void;
}
type MessageEventDataType<T> =
| {
type: 'status';
isLoading?: boolean;
isError?: any | false;
}
| {
type: 'response';
data: T;
};
export function useApiWorker<T>({
enabled,
url,
method,
transformResponse,
}: Props): UseApiWorker<T> {
const workerRef = useRef<Worker | null>(null);
const [data, setData] = React.useState<T | null>(null);
const [isLoading, setIsLoading] = React.useState<boolean>(false);
const [error, setError] = React.useState<any | null>(null);
const getAxiosConfig = useCallback(() => {
return { config: { ...axios.config, url }, method, transformResponse };
}, [method, transformResponse, url]);
React.useEffect(() => {
if (!enabled) {
return;
}
// Create the web worker
const worker = new Worker(new URL('./api.worker.js', import.meta.url));
workerRef.current = worker;
worker.postMessage(getAxiosConfig());
// Define the function to handle messages from the worker
function handleWorkerMessage(event: MessageEvent<MessageEventDataType<T>>) {
const data = event.data;
console.log('Received data from worker:', data);
if (!data) {
return;
}
if (data.type === 'status') {
if (typeof data.isLoading !== 'undefined') setIsLoading(data.isLoading);
if (typeof data.isError !== 'undefined') setError(data.isError);
}
if (data.type === 'response')
// Update your React component's state or perform other actions with the data
setData(data.data);
}
// Listen for messages from the worker
worker.onmessage = handleWorkerMessage;
// Clean up the worker when the component unmounts
return () => {
worker.terminate();
workerRef.current = null;
};
}, [enabled, getAxiosConfig]);
const handleRefetch = useCallback(
(axiosRequestConfig: AxiosRequestConfig) => {
if (workerRef.current)
workerRef.current.postMessage({ ...getAxiosConfig(), ...axiosRequestConfig });
},
[getAxiosConfig]
);
return { data, isLoading, error, refetch: handleRefetch };
}
export default useApiWorker;
// workers/api.worker.js
/* eslint-disable no-restricted-globals */
import axios from 'axios';
/**
*
* @param {MessageEvent<{config: {defaultHeaders: {Authorization: string}, baseURL: string, url: string},} & import('axios').AxiosRequestConfig>} event
*/
self.onmessage = function ({ data: { config, ...rest } }) {
if (!config) return;
// Define the function to fetch data from the API
async function fetchDataFromAPI() {
self.postMessage({ type: 'status', isLoading: true, isError: false });
try {
const res = await axios.request(config.url, {
...rest,
baseURL: config.baseURL,
headers: { ...rest.headers, ...config.defaultHeaders },
});
self.postMessage({ type: 'response', data: res.data.package }); // Send the data back to the main thread
} catch (error) {
console.error(`Error: [${self.name}]`, error);
self.postMessage({ type: 'status', isError: error }); // Send the data back to the main thread
} finally {
self.postMessage({ type: 'status', isLoading: false });
}
}
fetchDataFromAPI();
};
I just created it, have not tested it. But it should work. There might be minor bugs which I will fix when I will use it.
Hope it helps & Suggestions always accepted. As well as bug fixes. Thanks already!
Top comments (0)