///
import { use as originalUse, useContext } from 'react';
const STATUS = {
PENDING: 'pending',
REJECTED: 'rejected',
FULFILLED: 'fulfilled',
} as const;
type TState =
| { status: typeof STATUS.PENDING; promise: Promise }
| { status: typeof STATUS.REJECTED; error: Error }
| { status: typeof STATUS.FULFILLED; result: T };
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const states = new Map, TState>();
function usePromiseFallback(usable: Promise): T {
const existingState = states.get(usable);
const state: TState =
existingState ||
(() => {
const promise = usable
.then((data) => {
states.set(usable, {
status: STATUS.FULFILLED,
result: data,
});
})
.catch((error) => {
states.set(usable, {
status: STATUS.REJECTED,
error,
});
});
const newState: TState = { status: 'pending', promise: promise };
states.set(usable, newState);
return newState;
})();
switch (state.status) {
case STATUS.PENDING:
// Suspend the component while fetching
throw state.promise;
case STATUS.REJECTED:
// Result is an error
throw state.error;
case STATUS.FULFILLED:
// Result is a fulfilled promise
return state.result;
}
}
function useContextFallback(context: React.Context) {
return useContext(context);
}
function useFallback(usable: Promise | React.Context): T {
if (usable instanceof Promise) {
// eslint-disable-next-line react-hooks/rules-of-hooks
return usePromiseFallback(usable);
}
// eslint-disable-next-line react-hooks/rules-of-hooks
return useContextFallback(usable);
}
const use = originalUse || useFallback;
export default use;