|
|
|
@ -1,4 +1,4 @@
@@ -1,4 +1,4 @@
|
|
|
|
|
import { useCallback, useState } from "react"; |
|
|
|
|
import { useCallback, useReducer, useState } from "react"; |
|
|
|
|
import useMountedRef from "./useMountedRef"; |
|
|
|
|
|
|
|
|
|
interface State<D> { |
|
|
|
@ -17,32 +17,49 @@ const defaultConfig = {
@@ -17,32 +17,49 @@ const defaultConfig = {
|
|
|
|
|
throwOnError: false, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const useSafeDispatch = <T>(dispatch: (...args: T[]) => void) => { |
|
|
|
|
const mountedRef = useMountedRef(); |
|
|
|
|
|
|
|
|
|
return useCallback( |
|
|
|
|
(...args: T[]) => { |
|
|
|
|
mountedRef.current && dispatch(...args); |
|
|
|
|
}, |
|
|
|
|
[mountedRef, dispatch] |
|
|
|
|
); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const useAsync = <D>( |
|
|
|
|
initialState?: State<D>, |
|
|
|
|
initialConfig?: typeof defaultConfig |
|
|
|
|
) => { |
|
|
|
|
const config = { ...defaultConfig, initialConfig }; |
|
|
|
|
const [state, setState] = useState<State<D>>({ |
|
|
|
|
...defaultInitialState, |
|
|
|
|
...initialState, |
|
|
|
|
}); |
|
|
|
|
const mountedRef = useMountedRef(); |
|
|
|
|
const [state, dispatch] = useReducer( |
|
|
|
|
(state: State<D>, action: Partial<State<D>>) => ({ ...state, ...action }), |
|
|
|
|
{ |
|
|
|
|
...defaultInitialState, |
|
|
|
|
...initialState, |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
const safeDispatch = useSafeDispatch(dispatch); |
|
|
|
|
|
|
|
|
|
const setData = useCallback( |
|
|
|
|
(data: D) => setState({ data, stat: "success", error: null }), |
|
|
|
|
[] |
|
|
|
|
(data: D) => safeDispatch({ data, stat: "success", error: null }), |
|
|
|
|
[safeDispatch] |
|
|
|
|
); |
|
|
|
|
const setError = useCallback( |
|
|
|
|
(error: Error) => { |
|
|
|
|
safeDispatch({ data: null, stat: "error", error }); |
|
|
|
|
}, |
|
|
|
|
[safeDispatch] |
|
|
|
|
); |
|
|
|
|
const setError = useCallback((error: Error) => { |
|
|
|
|
setState({ data: null, stat: "error", error }); |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
const run = useCallback( |
|
|
|
|
(promise: Promise<D>) => { |
|
|
|
|
if (!promise || !promise.then) throw new Error("请传入 Promise 数据类型"); |
|
|
|
|
setState((prevState) => ({ ...prevState, stat: "loading" })); |
|
|
|
|
safeDispatch({ stat: "loading" }); |
|
|
|
|
return promise |
|
|
|
|
.then((data) => { |
|
|
|
|
if (mountedRef.current) setData(data); |
|
|
|
|
setData(data); |
|
|
|
|
return data; |
|
|
|
|
}) |
|
|
|
|
.catch((error) => { |
|
|
|
@ -51,7 +68,7 @@ const useAsync = <D>(
@@ -51,7 +68,7 @@ const useAsync = <D>(
|
|
|
|
|
return error; |
|
|
|
|
}); |
|
|
|
|
}, |
|
|
|
|
[config.throwOnError, mountedRef, setData, setData, setError] |
|
|
|
|
[config.throwOnError, setData, setData, safeDispatch, setError] |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|