46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
import { type DependencyList, useEffect, useState } from "react";
|
|
|
|
/**
|
|
* A custom hook that memoizes the result of an async function.
|
|
* It's a combination of useMemo and useEffect for asynchronous operations.
|
|
*
|
|
* @param asyncFactory A function that returns a Promise for the desired value.
|
|
* @param deps The dependency array. The async function is re-run when these change.
|
|
* @param initialValue An optional initial value to return while the promise is pending.
|
|
* @returns The resolved value of the promise, or the initialValue/undefined while pending.
|
|
*/
|
|
export function useAsyncMemo<T>(
|
|
asyncFactory: () => Promise<T>,
|
|
deps: DependencyList,
|
|
initialValue?: T,
|
|
): T | undefined {
|
|
const [value, setValue] = useState<T | undefined>(initialValue);
|
|
|
|
useEffect(() => {
|
|
let isCancelled = false;
|
|
|
|
asyncFactory()
|
|
.then((result) => {
|
|
if (!isCancelled) {
|
|
setValue(result);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
// It's good practice to handle potential errors
|
|
console.error("Error in useAsyncMemo:", error);
|
|
if (!isCancelled) {
|
|
// You might want to set an error state here
|
|
// For now, we'll just keep the old/initial value
|
|
}
|
|
});
|
|
|
|
// Cleanup function: If dependencies change before the promise resolves,
|
|
// this prevents setting state on an unmounted component or from a stale request.
|
|
return () => {
|
|
isCancelled = true;
|
|
};
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, deps);
|
|
|
|
return value;
|
|
}
|