@supunlakmal/hooks
Version:
A collection of reusable React hooks
82 lines • 3.12 kB
JavaScript
import { useCallback, useEffect, useRef, useState } from 'react';
/**
* `useWebWorker` is a custom React hook that allows you to run a function in a web worker,
* to avoid blocking the main thread.
*
* @param workerFunction - The function to run in the web worker.
* @returns An object containing the result, error, loading state, and functions to execute or terminate the worker.
*/
export const useWebWorker = (workerFunction) => {
const [workerState, setWorkerState] = useState({
result: null,
error: null,
isLoading: false,
});
const workerRef = useRef(null);
const workerFunctionRef = useRef(workerFunction);
useEffect(() => {
workerFunctionRef.current = workerFunction;
}, [workerFunction]);
const executeWorker = useCallback((...args) => {
setWorkerState((prev) => (Object.assign(Object.assign({}, prev), { isLoading: true, error: null })));
const workerCode = `
onmessage = (event) => {
try {
const workerFunction = (${workerFunctionRef.current.toString()});
const result = workerFunction(...event.data);
postMessage({ type: 'success', result });
} catch (error) {
postMessage({ type: 'error', error: error.message });
}
};
`;
const blob = new Blob([workerCode], { type: 'text/javascript' });
const url = URL.createObjectURL(blob);
workerRef.current = new Worker(url);
workerRef.current.onmessage = (event) => {
var _a;
if (event.data.type === 'success') {
setWorkerState({
result: event.data.result,
error: null,
isLoading: false,
});
}
else if (event.data.type === 'error') {
setWorkerState({
result: null,
error: new Error(event.data.error),
isLoading: false,
});
}
URL.revokeObjectURL(url);
(_a = workerRef.current) === null || _a === void 0 ? void 0 : _a.terminate();
};
workerRef.current.onerror = (error) => {
var _a;
setWorkerState({
result: null,
error: new Error(error.message),
isLoading: false,
});
URL.revokeObjectURL(url);
(_a = workerRef.current) === null || _a === void 0 ? void 0 : _a.terminate();
};
workerRef.current.postMessage(args);
}, []);
const terminateWorker = useCallback(() => {
if (workerRef.current) {
workerRef.current.terminate();
workerRef.current = null;
setWorkerState((prev) => (Object.assign(Object.assign({}, prev), { isLoading: false })));
}
}, []);
useEffect(() => {
return () => {
terminateWorker();
};
}, [terminateWorker]);
return Object.assign(Object.assign({}, workerState), { executeWorker,
terminateWorker });
};
//# sourceMappingURL=useWebWorker.js.map