mirror of
https://github.com/misode/misode.github.io.git
synced 2026-04-24 23:56:51 +00:00
Add useAsync hook
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
export * from './useActiveTimout'
|
||||
export * from './useAsync'
|
||||
export * from './useAsyncFn'
|
||||
export * from './useCanvas'
|
||||
export * from './useFocus'
|
||||
export * from './useHash'
|
||||
|
||||
17
src/app/hooks/useAsync.ts
Normal file
17
src/app/hooks/useAsync.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import type { Inputs } from 'preact/hooks'
|
||||
import { useEffect } from 'preact/hooks'
|
||||
import type { AsyncState } from './useAsyncFn'
|
||||
import { useAsyncFn } from './useAsyncFn'
|
||||
|
||||
export function useAsync<T>(
|
||||
fn: () => Promise<T>,
|
||||
inputs: Inputs = [],
|
||||
): AsyncState<T> {
|
||||
const [state, callback] = useAsyncFn<T, () => Promise<T>>(fn, inputs, { loading: true })
|
||||
|
||||
useEffect(() => {
|
||||
callback()
|
||||
}, [callback])
|
||||
|
||||
return state
|
||||
}
|
||||
59
src/app/hooks/useAsyncFn.ts
Normal file
59
src/app/hooks/useAsyncFn.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { Inputs } from 'preact/hooks'
|
||||
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
|
||||
|
||||
|
||||
export type AsyncState<T> = {
|
||||
loading: boolean,
|
||||
error?: undefined,
|
||||
value?: undefined,
|
||||
} | {
|
||||
loading: true,
|
||||
error?: Error | undefined,
|
||||
value?: T,
|
||||
} | {
|
||||
loading: false,
|
||||
error: Error,
|
||||
value?: undefined,
|
||||
} | {
|
||||
loading: false,
|
||||
error?: undefined,
|
||||
value: T,
|
||||
}
|
||||
|
||||
export function useAsyncFn<R, T extends (...args: any[]) => Promise<R>>(
|
||||
fn: T,
|
||||
inputs: Inputs = [],
|
||||
initialState: AsyncState<R> = { loading: false },
|
||||
): [AsyncState<R>, (...args: Parameters<T>) => Promise<R | undefined>] {
|
||||
const [state, setState] = useState<AsyncState<R>>(initialState)
|
||||
const isMounted = useRef<boolean>(false)
|
||||
const lastCallId = useRef(0)
|
||||
|
||||
useEffect(() => {
|
||||
isMounted.current = true
|
||||
return () => isMounted.current = false
|
||||
}, [])
|
||||
|
||||
const callback = useCallback((...args: Parameters<T>): Promise<R | undefined> => {
|
||||
const callId = ++lastCallId.current
|
||||
if (!state.loading) {
|
||||
setState(prev => ({ ...prev, loading: true }))
|
||||
}
|
||||
|
||||
return fn(...args).then(
|
||||
value => {
|
||||
if (isMounted.current && callId === lastCallId.current) {
|
||||
setState({ value, loading: false })
|
||||
}
|
||||
return value
|
||||
},
|
||||
error => {
|
||||
if (isMounted.current && callId === lastCallId.current) {
|
||||
setState({ error, loading: false })
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
}, inputs)
|
||||
|
||||
return [state, callback]
|
||||
}
|
||||
Reference in New Issue
Block a user