使用异步Effect的自定义Hook
function useAsync(asyncCallback) {
let [state, dispatch] = React.useReducer(asyncReducer)
React.useEffect(() => {
let promise = asyncCallback()
if (!promise) return
dispatch({ type: 'pending' })
promise
.then((data) => dispatch({ type: 'resolved', data }))
.catch((error) => dispatch({ type: 'rejected', error }))
}, [asyncCallback])
return state
}
使用方法:
function Component({ input }) {
// 记住要将异步任务包装在useCallback中
let asyncCallback = React.useCallback(() => {
if (!input) return
// 运行异步Effect(fetch只是一个示例)
return fetch(input)
}, [input])
let { status, data, error } = useAsync(asyncCallback)
switch (status) {
case 'idle':
return '等待异步任务触发'
case 'pending':
return '加载中界面'
case 'rejected':
throw error
case 'resolved':
return '数据界面'
default:
throw new Error('这种情况不应该发生')
}
}
如何清理副作用(异步任务开始后组件卸载)?
- 使用
useSafeDispatch
!
function useSafeDispatch(dispatch) {
let mountedRef = React.useRef(false)
React.useEffect(() => {
mountedRef.current = true
return () => {
mountedRef.current = false
}
}, [])
return React.useCallback(
(...args) => {
if (mountedRef.current) {
dispatch(...args)
}
},
[dispatch]
)
}
现在修改 useAsync
函数:
function useAsync(asyncCallback) {
let [state, unsafeDispatch] = React.useReducer(asyncReducer)
let dispatch = useSafeDispatch(unsafeDispatch)
React.useEffect(() => {
let promise = asyncCallback()
if (!promise) return
dispatch({ type: 'pending' })
promise
.then((data) => dispatch({ type: 'resolved', data }))
.catch((error) => dispatch({ type: 'rejected', error }))
}, [asyncCallback])
return state
}
还需要定义 asyncReducer
:
function asyncReducer(state, action) {
switch (action.type) {
case 'pending': {
return { status: 'pending', data: null, error: null }
}
case 'resolved': {
return { status: 'resolved', data: action.data, error: null }
}
case 'rejected': {
return { status: 'rejected', data: null, error: action.error }
}
default: {
return { status: 'idle', data: null, error: null }
}
}
}
Cheers