1

I use the onGetAccount function to get the address of the logged user:

const [userWalletInfo, setUserWalletInfo] = useState()

         
  async function onGetAccount() {
    const account = await client.api.asset.getAccount();
    
    return account;
    
  }  
  useEffect(() => {
    async function fetchUserWalletInfo() {
      const address = await onGetAccount();
      
      setUserWalletInfo({
        address: address,
        
      });
    }
    fetchUserWalletInfo();
  }, []);

By doing so I am able to access userWalletInfo.address.

The problem is, if I load the component and after that (I mean that while the component run on localhost I edit code in VSCode, I did it to understand if address was set correctly) I edit the js. file adding:

<div> {userWalletInfo.address} </div>

It displays user address correctly, but if I refresh the page I get "TypeError: Cannot read property 'address' of undefined".
From this I infer that the page is rendered before fetchUserWalletInfo() runs out.

Kevin Nett
  • 407
  • 8
  • 2
    You could use the safe navigation operator:
    {userWalletInfo?.address}
    . What exactly is your question though?
    – Jordan Stubblefield Dec 03 '20 at 19:27
  • It wokrs, can you explain me why? I mean what is `?` for? – Kevin Nett Dec 03 '20 at 19:31
  • 1
    `?` is the optional chaining operator. It basically works like an `if ` statement. i.e. If `userWalletInfo` object exists, then retrieve the `address` property. I've explained the same in my answer below. – Link Dec 03 '20 at 19:50
  • Also, `?` is ES 2020 syntax, therefore you need to make sure you are using Bable in order to polyfill it. If you're using create react app, NextJS or something like that you most likely don't need to worry about this, but if you configured the React app from scratch you need to keep this in mind before shipping it – Link Dec 03 '20 at 19:52
  • 1
    The `?.` operator is more commonly referred as "optional chaining operator". It’s just a syntax sugar that perform a nullish check to the left hand side operand before accessing its property, so you don’t get runtime error like the one you’ve got. – hackape Dec 03 '20 at 19:52

2 Answers2

3

Change your div to only display when userWalletInfo has an address, this will avoid the render function to attempt accessing a property on an undefined object.

<div> {userWalletInfo && userWalletInfo.address} </div>

More details:

At the beginning userWalletInfo is undefined, hence trying to access its address will drive the error you are encountering.

The render function is being called (i.e. your div) before there is a populated value in userWalletInfo, since the population is happening on an async call, and React render attempts to render the component regardless of async calls that may happen in the background.

The solution provided works since it provides an extra cautious when trying to display userWalletInfo, it will attempt to access userWalletInfo address only when userWalletInfo isn't undefined, so that solves the failure of the render function before the value is populated.

Shimi
  • 980
  • 5
  • 14
3

"TypeError: Cannot read property 'address' of undefined".

What the above error message essentially means is that he userWalletInfo object is undefined and there is no address property on undefined.

The reason for userWalletInfo being undefined is because you are fetching it using an async function, what it means is that when your render() first executes, the value of userWalletInfo is still undefined.

Why is userWalletInfo undefined on initial render?

Because, although you might've made the async function call already, the async functions are not executed on the Javascript main thread, instead they are sent to a WEB API environment as soon as the line of code containing the async function call is executed.

However, the code outside the async function call is still synchronous, which means it will execute and not wait for the async call to complete and return the data.

Now, in order to not have this error anymore, all you need to do is have a conditional statement. i.e, you will need to check if the userWalletInfo object exists before trying to retrieve the address property to show on the DOM.

You can do that in multiple ways:

  1. Have a simple if statement
  2. Use optional chaining operator ? like so: userWalletInfo?.address (this just acts like an if-statement. However this is ES 2020 syntax therefore you will need to polyfill if you want to support old browsers).
  3. Use Short-circuiting like user @Shimi has mentioned in her answer.
  4. Let userWalletInfo defaults to empty object. const [userWalletInfo] = useState({}). The effect is similar to optional chaining in that it prevents accessing property of undefined runtime error.

If you want to know more details about how the Javascript event loop and asynchronous function calls work, please read my answer from here: How Async calls, event loop and callback queue work.

hackape
  • 11,966
  • 1
  • 15
  • 40
Link
  • 1,048
  • 1
  • 14