React Hook:支持 async 的 useEffect
前言
众所周知,useEffect接受的回调函数可以返回一个函数进行清除副作用。
ts- useEffect(() => {
- const timer = setInterval(() => {
- console.log('miao')
- }, 1000)
- return () => {
- clearInterval(timer)
- }
- }, [])
当回调函数是具有async修饰符时:
ts- useEffect(async () => {
- const data = await getData()
- }, [])
useEffect会返回如下报错
cmd- Warning: An effect function must not return anything besides a function, which is used for clean-up
带async的函数返回一个Promise,燃鹅useEffect要求返回一个函数。
怎么支持 async
可以在回调函数中手动执行一下:
ts- useEffect(() => {
- const func = async () => {
- // ...
- }
- func()
- }, [])
也有IIFE(Immediately Invoked Function Expression)的形式:
ts- useEffect(() => {
- async () => {
- // ...
- }()
- }, [])
可以在返回的函数中调用then方法处理副作用:
ts- useEffect(() => {
- const func = async () => {
- // ...
- }
- const ans = func()
- return () => {
- ans.then(res => {
- // ...
- })
- }
- }, [])
自定义 hook
我们不妨抽取相关逻辑来自定义一个 hook。ahook 中的useAsyncEffect就提供了对async的支持,这里也实现一个类似的钩子。有意思的是,ahook 中的useAsyncEffect提供了对AsyncGenerator(这是 ES9 新增的东西,详见文档)的支持。AsyncGenerator是一个支持async、await的生成器,这里也来支持一下。
ts- export type asyncEffect = (arg?: unknown) => PromiseLike<unknown> | AsyncGenerator
- export const isAsyncGenerator = (effect: unknown): effect is AsyncGenerator => {
- return effect instanceof Object && typeof effect[Symbol.asyncIterator] === 'function'
- }
- export const asyncEffect = (
- effectHook: (effect: React.EffectCallback, deps: React.DependencyList) => void
- ) => (
- effect: asyncEffect, deps: React.DependencyList
- ) => {
- const callback = useRef(effect)
- // 记录 useEffect 的清除函数有没有被调用,副作用被清除后就不应该执行剩下的 AsyncGenerator 了
- const canceled = useRef(false)
- useEffect(() => {
- callback.current = effect
- }, [effect])
- const run = useCallback(async () => {
- canceled.current = false
- if (isAsyncGenerator(callback.current)) {
- while (true) {
- const ans = await callback.current.next()
- if (ans.done || canceled.current) {
- return ans.value
- }
- }
- } else {
- return await callback.current()
- }
- }, [])
-
- effectHook(() => {
- const cleanEffect = run()
- return () => {
- canceled.current = true
- // 这里支持函数返回一个 fulfilled 状态的 Promise 来清除副作用
- cleanEffect.then(clean => {
- typeof clean === 'function' && clean()
- })
- }
- }, deps)
- }
- export const useAsyncEffect = asyncEffect(useEffect)
- export const useAsyncUpdateEffect = asyncEffect(useUpdateEffect)
0 条评论未登录用户
Ctrl or + Enter 评论
