Appearance
useAsync
Derived value for async functions
Demo
Usage
tsx
import { useAsync } from '@reause/core'
import { useState } from 'react'
const [name, setName] = useState('jack')
const userInfo = useAsync(
async () => {
return await mockLookUp(name)
},
null, // initial state
{ deps: [name] },
)Evaluation State
Use the onEvaluating callback to track if the async function is currently evaluating.
tsx
import { useAsync } from '@reause/core'
import { useState } from 'react'
const [evaluating, setEvaluating] = useState(false)
const userInfo = useAsync(
async () => { /* your logic */ },
null,
{ onEvaluating: setEvaluating },
)onCancel
When the derived value's dependencies change before the previous async function resolves, you may want to cancel the previous one. Here is an example showing how to incorporate with the fetch API.
tsx
import { useAsync } from '@reause/core'
import { useState } from 'react'
const [packageName, setPackageName] = useState('@reause/core')
const downloads = useAsync(async (onCancel) => {
const abortController = new AbortController()
onCancel(() => abortController.abort())
return await fetch(
`https://api.npmjs.org/downloads/point/last-week/${packageName}`,
{ signal: abortController.signal },
)
.then(response => response.ok ? response.json() : { downloads: '—' })
.then(result => result.downloads)
}, 0, { deps: [packageName] })Lazy
By default, useAsync will start resolving immediately on creation. Specify skipInitial: true to skip the initial evaluation and start resolving only when deps change.
tsx
import { useAsync } from '@reause/core'
import { useState } from 'react'
const [evaluating, setEvaluating] = useState(false)
const userInfo = useAsync(
async () => { /* your logic */ },
null,
{ skipInitial: true, onEvaluating: setEvaluating },
)Error Handling
Use the onError callback to handle errors from the async function.
tsx
import { useAsync } from '@reause/core'
import { useState } from 'react'
const [name, setName] = useState('jack')
const userInfo = useAsync(
async () => {
return await mockLookUp(name)
},
null,
{
deps: [name],
onError(e) {
console.error('Failed to fetch user info', e)
},
},
)Shallow Ref
By default, upstream uses shallowRef internally. React state is always a fresh object, so the shallow-ref caveat does not apply.
Caveats
- Just like an effect keyed by
deps,useAsyncre-evaluates when the dependencies in thedepsarray change. Note however that only dependencies listed indepsare considered for this. In other words: Values that are accessed asynchronously inside the callback will not trigger re-evaluation of the derived value. - Re-evaluation of the derived value is triggered whenever the
depschange, regardless of whether its result is currently being used.
Type Declarations
ts
export type UseAsyncOnCancel = (cancelCallback: Fn) => void
export interface UseAsyncOptions {
deps?: unknown[];
onEvaluating?: (value: boolean) => void;
skipInitial?: boolean;
lazy?: boolean;
onError?: (error: unknown) => void;
}
type Fn = () => void
export function useAsync<T>(evaluationCallback: (onCancel: UseAsyncOnCancel) => T | Promise<T>, initialState: State<T>, options?: UseAsyncOptions): T
export function useAsync<T>(evaluationCallback: (onCancel: UseAsyncOnCancel) => T | Promise<T>, initialState?: undefined, options?: UseAsyncOptions): T | undefined