Published on

useAsyncEffect

Authors
  • avatar
    Name
    李丹秋
    Twitter

基础用法

import { useAsyncEffect } from 'front-base-hooks';
import React, { useState } from 'react';

function mockCheck(): Promise<boolean> {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(true);
    }, 3000);
  });
}

export default () => {
  const [pass, setPass] = useState<boolean>();

  useAsyncEffect(async () => {
    setPass(await mockCheck());
  }, []);

  return (
    <div>
      {pass === undefined && 'Checking...'}
      {pass === true && 'Check passed.'}
    </div>
  );
};

如果我们直接在Effect中使用async例如

const UsersComponent = () => {
  const [users, setUsers] = useState([]);
  const [usersLoading, setUsersLoading] = useState(false);

  useEffect(async () => {
    setUsersLoading(true);
    const result = await getUsers();
    setUsers(result);
    setUsersLoading(false);
  }, []);

  return (
    <div>
      <h1>Users</h1>
      <div>
        {users.map((user) => (
          <div key={user.id}>{user.username}</div>
        ))}
      </div>
    </div>
  );
};

会提醒useEffect must not return anything besides a function, which is used for clean-up这样的错误

查看useEffect源码得出,useEffect传递的回调函数,返回值必须限定为void或者Destructor,不能是Promise或者迭代器

type EffectCallback = () => (void | Destructor)
type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never }

关于useEffect处理异步的文章

源码

import type { DependencyList } from 'react';
import { useEffect } from 'react';
import { isFunction } from '../utils';

function isAsyncGenerator(
    val: AsyncGenerator<void, void, void> | Promise<void>,
): val is AsyncGenerator<void, void, void> {
    return isFunction(val[Symbol.asyncIterator]);
}

function useAsyncEffect(
    effect: () => AsyncGenerator<void, void, void> | Promise<void>,
    deps?: DependencyList,
) {
    useEffect(() => {
        const e = effect();
        let cancelled = false;
        async function execute() {
            if (isAsyncGenerator(e)) {
                // eslint-disable-next-line no-constant-condition
                while (true) {
                    const result = await e.next();
                    if (result.done || cancelled) {
                        break;
                    }
                }
            } else {
                await e;
            }
        }
        execute();
        return () => {
            cancelled = true;
        };
    }, deps);
}

export default useAsyncEffect;

关于 Interface AsyncGenerator

所以解决方案就是自己再包一层异步函数,然后再执行