Reputation: 429
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.
Upvotes: 2
Views: 498
Reputation: 1248
"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:
?
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).Short-circuiting
like user @Shimi has mentioned in her answer.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.
Upvotes: 4
Reputation: 1218
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.
Upvotes: 3