I have a contacts
variable that is set by calling an API. This is all done in an immediately invoked function expression (async () => {})();
. I have to get a token to access the Microsoft Graph API. This lets me call an read the selected email using the Office.js library await getMessage(microsoftGraphAccessToken...)
.
I call my own API endpoint getContactsByEmail
. The contacts
variable contains data, but the contactDropdown
does not show – it just remains as <></>
.
const App = () => {
let contacts = null;
let contactDropdown = <></>;
(async () => {
let microsoftGraphAccessToken = null;
let message = null;
await Office.onReady();
//Check if Graph is already authorized
const graphTokenResponse = await graphAuth.getToken(graphLoginRequest);
microsoftGraphAccessToken = graphTokenResponse.accessToken;
await getMessage(microsoftGraphAccessToken, Office.context.mailbox.item.itemId, async (data) => {
message = data;
});
const contacts = await getContactsByEmail(message.from.emailAddress.address);
if (contacts.length > 0) {
contactDropdown = (
<select>
{contacts.map((contact) => (
<option key={contact.id} value={contact.id}>
{contact.name}
</option>
))}
</select>
);
}
})();
return (
<div>
{contactDropdown}
</div>
);
}
2
Answers
Thanks for the help @David. I moved
contacts
to a state variable. I converted my IIFE to a function and I am now using that insideuseEffect
.The issue I was having is I was rendering an element from a variable:
let contactDropdown = <></>;
but I am now directly rendering it.I was wondering if there was a reason for this though? Maybe I am misunderstanding the point in time when
contactDropdown
is being rendered.In whatever React tutorial(s) you’re using, you’ve overlooked the single most fundamental concept in React… state.
The initial value for the variable
contactDropdown
is<></>
. So that’s what gets rendered in the component when it first renders. Then at a later time you update the value for that variable. However, directly updating a variable does not trigger React to re-render the component. Updating state does.Put the variable in state:
Then use the state setter when you need to update it at a later time:
Invoking that state setter function tells React to queue a re-render after updating the state value.
As an aside… In general it’s uncommon to store markup in state. Instead, you should store data in state and generate the markup in the rendering. What you have should work, but could easily run into problems if you ever need to do anything else with that data.
For example, store the "contacts" in state:
And update it with your fetched data:
Then use that data to render the markup in your component:
Additionally, and this is important… I just realized your component will infinitely re-render. Don’t make API calls like that directly in the component body. They’ll be invoked on every render. And if that operation itself triggers a re-render, then every render will trigger a re-render.
To perform an API call once when the component first renders, wrap it in
useEffect
with an empty dependency array: