最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Struggling with async custom react hooks. How do i await result of one hook to use in another hook? - Stack Overflo

programmeradmin7浏览0评论

I'm struggling a bit with using custom react hooks.

I got 2 custom hooks.

First hook is for fetching a ID, the second one is used to fetch a profile with this previous fetched ID. It is dependent on that ID so I need to await this promise.

I have the following custom hook:

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();
}

Then in my second hook I have:

const wallet = UseMetamask();

which is then used in a react-query call like:

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

Now it plains on the wallet about it being a Promise<string | null> which is ofcourse not suitable for the getUserByWallet.

What is the go to way to wait for another hook then use that result in a second hook?

Thanks!

I'm struggling a bit with using custom react hooks.

I got 2 custom hooks.

First hook is for fetching a ID, the second one is used to fetch a profile with this previous fetched ID. It is dependent on that ID so I need to await this promise.

I have the following custom hook:

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();
}

Then in my second hook I have:

const wallet = UseMetamask();

which is then used in a react-query call like:

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

Now it plains on the wallet about it being a Promise<string | null> which is ofcourse not suitable for the getUserByWallet.

What is the go to way to wait for another hook then use that result in a second hook?

Thanks!

Share Improve this question edited Feb 3, 2023 at 11:11 Ori Drori 192k32 gold badges237 silver badges227 bronze badges asked Feb 3, 2023 at 9:04 Wesley van SWesley van S 1271 gold badge2 silver badges13 bronze badges 2
  • Your first hook isn't using any react state or methods. It's better served as a normal function. You can use await on the second parameter of useQuery – Badal Saibo Commented Feb 3, 2023 at 9:11
  • @BadalSaibo Interesting! This might just be the trick. Making the queryFn async will work I think. – Wesley van S Commented Feb 3, 2023 at 9:19
Add a ment  | 

3 Answers 3

Reset to default 13

A functional ponent is a synchronous function, and as a ponent has life cycle hooks. The asynchronous calls are side effects that should be handled by hooks, not by passing promises in the body of the function. See this SO answer.

Option 1 - using useEffect with useState:

Wrap the api call in useEffect and set the wallet state when the api call succeeds. Return the wallet state from the hook:

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;
}

Usage:

Get the wallet from the hook. This would be null or the actual value:

const wallet = useMetamask();

Only enable the call when a wallet actually exists (not null). We'll use the enable option (see Dependent Queries), to enable/disable the query according to the value of wallet:

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

Option 2 - use two useQuery hooks

Since you already use useQuery, you need to write a custom hook. Just get the wallet from another useQuery call:

const wallet = useQuery('wallet', fetchWallet);

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

It is a bad idea to create a hook then just return a single function out of it. And it is a promise too on top of that. Return an object from your hook instead. Then await it in your caller.

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 };
}

Then in your caller

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

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

Also, please use a small letter 'useSomething' in your hooks to differentiate it from your UI ponents

You need to use useState in the custom hook.

// 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 ponent updates, You want to do it only once.
    useEffect(()=>{
        fetchWallet().then(wallet => setWallet(wallet)).catch(errorHandler);
    }, [])
    return wallet;
}

In other hooks, check if wallet is null and handle accordingly.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论