0

I'm trying to use useEffect with some async functions, however when the dependency in the dependency array changes, only the non async code gets called in useEffect.

    useEffect( async () => {
        console.log(account);
        const web3 = await getWeb3();
        const acc = await loadAcc(web3);
        await loadContract(web3, acc);
    }, [account])

When my account state variable changes, useEffect gets invoked again, but only the console.log(account) statement will get executed.

How should I work around this problem?

code_learner93
  • 571
  • 5
  • 12

2 Answers2

2

The function passed into useEffect cannot be async. You can define an async function inside the useEffect and then use the then/catch syntax.

Further, also pass in all the functions that are defined outside of the useEffect (that you are calling) as a dependency to useEffect

useEffect(() => {

  const myAsyncFunc = async () => {
     console.log(account);
     const web3 = await getWeb3();
     const acc = await loadAcc(web3);
     await loadContract(web3, acc);
  }
  myAsyncFunc.catch(console.error);
       
}, [account, getWeb3, loadAcc, loadContract])
Michael Brenndoerfer
  • 3,483
  • 2
  • 39
  • 50
  • Will I be able to invoke useEffect again and the function if I just leave account in the dependecy array? – code_learner93 Jun 20 '22 at 06:24
  • 1
    Most likely it'd still work, as I wouldn't expect side effects/ any code changes from the lib functions that you are calling, but it's best practice to include all dependencies, as in the example above. You might not observe a useEffect trigger when nested fields in the account object changed (without replacing the account object). For that you could use https://github.com/kentcdodds/use-deep-compare-effect and read up on it here https://stackoverflow.com/questions/54095994/react-useeffect-comparing-objects – Michael Brenndoerfer Jun 20 '22 at 06:34
1

useEffect expected to return either void or a function( the cleanup function ). When you make the function you pass to useEffect as an async, the function will return a promise.

One way to do it is,

    useEffect( () => {
        const init = async () => {
          const web3 = await getWeb3();
          const acc = await loadAcc(web3);
          const res = await loadContract(web3, acc);
          // do something after the async req
        }
        init();
    }, [getWeb3, loadAcc, loadContract])

Or else,

const [web3, setWeb3] = useState(null);
const [acc, setAcc] = useState(null);

useEffect(() => {
  getWeb3();
}, [getWeb3])

useEffect(() => {
  if (!web3) return;
  loadAcc(web3);
}, [web3, loadAcc])

useEffect(() => {
  if (acc && web3) {
   loadContract(acc, web3);
  }
}, [acc, web3, loadContract])

const getWeb3 = useCallback(async () => {
   // do some async work
   const web3 = // async call
   setWeb3(web3)
}, [])

const loadAcc = useCallback(async (web3) => {
  // do some async work
  const acc = // async call
  setAcc(acc);
}, [])

const loadContract = useCallback(async (acc, web3) {
  // do anything
}, [])
Dilshan
  • 2,797
  • 1
  • 8
  • 26