npm i @react-hook/async
A React hook for gracefully resolving, cancelling, and handling errors for async functions and promises. These hooks also clean up any lingering promises when your component unmounts by cancelling them.
Check out the example on CodeSandbox
import {useAsync, useAsyncEffect} from '@react-hook/async'
// Example using a manual invocation
const CallbackResolver = () => {
const [{status, cancel, error, value}, call] = useAsync(() => {
return new Promise((resolve) => setTimeout(() => resolve('Loaded'), 3000))
})
switch (status) {
case 'loading':
return (
<div>
<button onClick={cancel}>Cancel</button>
Loading...
</div>
)
case 'cancelled':
return (
<div>
Cancelled.
<button onClick={call}>Try again</button>
</div>
)
case 'error':
return `Error: ${error}`
case 'success':
return value || 'Success!'
default:
return <button onClick={call}>Load me</button>
}
}
// Example using a useEffect invocation
const EffectResolver = () => {
const [curr, setCurr] = useState(0)
// Will load each time counter changes
const {status, cancel, error, value} = useAsyncEffect(() => {
return new Promise((resolve) =>
setTimeout(() => resolve(`Loaded ${curr}`), 3000)
)
}, [curr])
switch (status) {
case 'loading':
return (
<div>
<button onClick={cancel}>Cancel</button>
Loading...
</div>
)
case 'cancelled':
return (
<div>
Cancelled.
<button onClick={() => setCurr((curr) => ++curr)}>Try again</button>
</div>
)
case 'error':
return `Error: ${error}`
case 'success':
return (
<div>
{value}
<button onClick={() => setCurr((curr) => ++curr)}>Load again</button>
</div>
)
default:
return null
}
}
export const useAsync = <
ValueType extends any = any,
ErrorType extends any = Error,
Args extends any[] = any[]
>(
asyncCallback: (...args: Args) => Promise<ValueType>
): [AsyncState<ValueType, ErrorType, Args>, AsyncCallback<Args>]
Argument | Type | Default | Required? | Description |
---|---|---|---|---|
asyncCallback | (...args: Args) => Promise<ValueType> |
undefined |
Yes | An async function or function that returns a promise. |
Returns [AsyncState<ValueType, ErrorType>
, AsyncCallback
]
export interface AsyncState<ValueType, ErrorType> {
// 'idle' | 'loading' | 'success' | 'error' | 'cancelled'
status: AsyncStatus
// The return value of your async callback or promise. This value is persisted until there
// is another successful promise resolution. That means you when you're in an 'error', 'loading',
// or 'cancelled' state, you'll still have the most recent successful value here. This is useful
// because the status property should be dictating what you're doing in your UI and there are
// cases where you won't want to lose the current value.
value?: ValueType
// The error object from any exceptions encountered inside the async function
// or the value of the promise rejection.
error?: ErrorType
// Cancels the promise
cancel: () => void
}
export interface AsyncCallback<Args extends any[] = any[]> {
(...args: Args): void
cancel: () => void
}
export const useAsyncEffect = <
ValueType extends any = any,
ErrorType extends any = Error
>(
asyncCallback: () => Promise<ValueType>,
dependencies?: React.DependencyList
): AsyncState<ValueType, ErrorType, []>
This hook will invoke a callback each time its dependencies array changes.
Argument | Type | Default | Required? | Description |
---|---|---|---|---|
asyncCallback | () => Promise<ValueType> |
undefined |
Yes | An async function or function that returns a promise. |
dependencies | any[] |
undefined |
No | Values or state that your callback depends on. This works the same as the dependencies array of useEffect , useCallback , etc. |
Returns AsyncState<ValueType, ErrorType>
MIT