reactjs 正在纠结于异步自定义React钩子,我如何等待一个钩子的结果用于另一个钩子?

9njqaruj  于 2023-02-03  发布在  React
关注(0)|答案(3)|浏览(122)

我在使用自定义react钩子时遇到了一些困难。
我有两个定制的钩子。
第一个钩子用于获取一个ID,第二个钩子用于获取一个带有先前获取的ID的概要文件。它依赖于那个ID,所以我需要等待这个承诺。
我有以下自定义钩子:

export const UseMetamask = () => {

    //Todo: Create check if metamask is in browser, otherwise throw error
    const fetchWallet = async (): Promise<string | null> => {
        try {
            const accounts: string[] = await window.ethereum.request(
                {
                    method: 'eth_requestAccounts'
                },
            );

            return accounts[0];

        } catch(e) {
            console.error(e);

            return null;
        }
    }
    
    return fetchWallet();
}

然后在我的第二个钩子中,我有:

const wallet = UseMetamask();

然后将其用在如下的React查询调用中:

useQuery(
    ['user', wallet],
    () => getUserByWallet(wallet),

现在它在钱包上抱怨它是一个Promise<string | null>,这当然不适合getUserByWallet
等待另一个钩子然后在第二个钩子中使用该结果的方法是什么?
谢谢!

zbq4xfa0

zbq4xfa01#

一个函数组件是一个同步函数,并且作为一个组件有生命周期钩子。异步调用是副作用,应该由钩子处理,而不是通过在函数体中传递承诺。参见SO answer

    • 选项1-将useEffectuseState一起使用:**

将api调用 Package 在useEffect中,当api调用成功时设置wallet状态,从钩子返回wallet状态:

export const useMetamask = () => {
  const [wallet, setWallet] = useState<string | null>(null);
  
  useEffect(() => {
    const fetchWallet = async(): Promise<string | null> => {
      try {
        const accounts: string[] = await window.ethereum.request({
          method: 'eth_requestAccounts'
        });

        setWallet(accounts[0]);

      } catch (e) {
        console.error(e);

        return null;
      }
    }
    
    fetchWallet();
  }, []);

  return wallet;
}
    • 用法:**

从钩子中获取wallet,可以是null或实际值:

const wallet = useMetamask();

wallet实际存在(而不是null)时,只调用enable。我们将使用enable选项(参见Dependent Queries),根据wallet的值启用/禁用查询:

useQuery(
  ['user', wallet],
  () => getUserByWallet(wallet),
  {
  // The query will not execute until the wallet exists
  enabled: !!wallet,
  }
)
    • 选项2-使用两个useQuery挂钩**

由于您已经使用了useQuery,因此需要手动编写一个钩子,只需从另一个useQuery调用中获取wallet即可:

const wallet useQuery('wallet', fetchWallet);

useQuery(
  ['user', wallet],
  () => getUserByWallet(wallet),
  {
  // The query will not execute until the wallet exists
  enabled: !!wallet,
  }
)
wqlqzqxt

wqlqzqxt2#

创建一个钩子然后只从钩子返回一个函数是一个坏主意。这也是一个承诺。从你的钩子返回一个对象。然后在你的调用者中等待它。

export const useMetamask = () => {

    //Todo: Create check if metamask is in browser, otherwise throw error
    const fetchWallet = async (): Promise<string | null> => {
        try {
            const accounts: string[] = await window.ethereum.request(
                {
                    method: 'eth_requestAccounts'
                },
            );

            return accounts[0];

        } catch(e) {
            console.error(e);

            return null;
        }
    }
    
    return { fetchWallet };
}

然后在你的呼叫器中

const { fetchWallet } = useMetamask();
const wallet = await fetchWallet();

useQuery(
    ['user', wallet],
    () => getUserByWallet(wallet),

此外,请在钩子中使用小写字母“useSomething”,以将其与UI组件区分开来

wfsdck30

wfsdck303#

您需要在自定义钩子中使用useState。

// move fetchWallet function to utils and import it here for better code smells

export const useMetamask = () => {
    const [wallet, setWallet] = useState(null);
    // you do not want to fetch wallet everytime the component updates, You want to do it only once.
    useEffect(()=>{
        fetchWallet().then(wallet => setWallet(wallet)).catch(errorHandler);
    }, [])
    return wallet;
}

在其他钩子中,检查wallet是否为空并进行相应处理。

相关问题