难点
如何在发送请求后实时更新数据
use-async.tsx
//? 处理 loading error
import { useState } from "react"
interface State<D> {
data: D | null,
error: Error | null,
stat: "idle" | "loading" | "error" | "success"
}
const defaultInitralState: State<null> = {
data: null,
error: null,
stat: 'idle'
}
// *配置参数 是否手动抛出错误
const defaultConfig = {
throwError: false
}
export const useAsync = <D>(initialState?: State<D>, initialConfig?: typeof defaultConfig) => {
const config = {
...defaultConfig,
...initialConfig
}
const [state, setState] = useState({
...defaultInitralState,
...initialState
})
const setDate = (data: D) => setState({
data,
error: null,
stat: 'success'
})
const setError = (error: Error) => setState({
data: null,
error,
stat: 'error'
})
// *发起异步请求
const run = (promise: Promise<D>) => {
if (!promise || !promise.then) {
throw new Error('请传入 Promise 类型数据')
}
setState({ ...state, stat: "loading" })
return promise.then(data => {
setDate(data)
return data
}).catch(error => {
setError(error)
if (config.throwError)
return Promise.reject(error)
return error
})
}
return {
isLoading: state.stat === 'loading',
isError: state.stat === 'error',
run,
...state
}
}
在发起请求后,我们如何实时的更新数据
方案一
我们可以重新请求 添加 retry 函数 ,即在run 后调用retry函数 重新请求数据。
但是我们该如何来写retry函数
如下所示:我们用useState来保存retry
...
const [retey,setRetry] = useState(()=>{})
// *发起异步请求
const run = (promise: Promise<D>) => {
if (!promise || !promise.then) {
throw new Error('请传入 Promise 类型数据')
}
setRetry(()=>{ run(promise) })
setState({ ...state, stat: "loading" })
return promise.then(data => {
setDate(data)
return data
}).catch(error => {
setError(error)
if (config.throwError)
return Promise.reject(error)
return error
})
}
return {
isLoading: state.stat === 'loading',
isError: state.stat === 'error',
run,
retry,
...state
}
}
如上,这样会有什么问题呢,根据useState的惰性初始化,initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用。
所以我们要这么写
...
const [retey,setRetry] = useState(()=>()=>{})
// *发起异步请求
const run = (promise: Promise<D>) => {
if (!promise || !promise.then) {
throw new Error('请传入 Promise 类型数据')
}
setRetry(()=>{()=>()=> run(promise) })
setState({ ...state, stat: "loading" })
return promise.then(data => {
setDate(data)
return data
}).catch(error => {
setError(error)
if (config.throwError)
return Promise.reject(error)
return error
})
}
...