react-exo-hooks
Version:
A collection of useful hooks for data structures and logic, designed for efficiency
58 lines (57 loc) • 2.07 kB
JavaScript
import { useEffect, useMemo, useReducer, useRef } from 'react';
/**
* A hook that dynamically refetches data on dependency update
* @note The first-order function runs on server-side and client-side and determines whether the async second-order function should run client-side
* @param fn The async function to run
* @param deps The dependencies
* @param persist Persist result values and error values into states that wouldn't normally have them
* @returns An object containing the state and settled values
*/
export function usePromise(fn, deps = [], persist) {
const value = useRef({
state: 'waiting',
result: undefined,
error: undefined
});
// Manage renders manually so everything can be a ref for instantaneous state changes
const [, rerender] = useReducer(() => ({}), {});
// useMemo runs before any other hook
const callback = useMemo(() => {
const cb = fn();
value.current = {
state: 'waiting',
result: persist ? value.current.result : undefined,
error: persist ? value.current.error : undefined
};
rerender();
return cb;
}, deps);
useEffect(() => {
if (!callback)
return;
const aborter = new AbortController();
callback(aborter.signal)
.then((result) => {
if (aborter.signal.aborted)
return; // Don't act upon result
value.current = {
state: 'resolved',
result,
error: persist ? value.current.error : undefined
};
rerender();
})
.catch((err) => {
if (!aborter.signal.aborted) {
value.current = {
state: 'rejected',
result: persist ? value.current.result : undefined,
error: err
};
rerender();
}
});
return () => aborter.abort();
}, [callback]);
return value.current;
}