I am learning React for a project at work and currently really having trouble getting the hang of the asynchronous model.
Here is the component I’m working on:
const Customers = () => {
const [logos, setLogos] = useState([]);
const [customers, setCustomers] = useState([]);
const [titlePrompt, setTitlePrompt] = useState([]);
const navigate = useNavigate();
useEffect(() => {
// create a customer client object
const client = new Client();
// query for the customer list and save name and token
if (customers.length === 0) {
client.get_customers().then((res) => {
const customer_info = res.map(customer => {
return {
'name': customer.customer_name,
'token': customer.mqtt_customer_token
}
});
setCustomers(customer_info);
});
}
if (customers.length > 0) {
const fetched_logos = [];
for (let i = 0; i < customers.length; i++) {
client.get_site_by_type(customers[i].token, 1).then((res) => {
const new_logo = {
[customers[i].name]: res.logo
};
fetched_logos.push(new_logo);
});
}
setLogos(fetched_logos);
}
setTitlePrompt("Select A Customer Shown Below");
}, [customers, logos]);
/**
* button handler to transition to a dataframe view of the chosen customer via token
* @param {defining} token
*/
const onButtonClick = (token) => {
navigate('/dataframe', { state: { token } });
};
return (
<div className={'mainContainer'}>
<div className={'contentContainer'}>
<div className={'titleContainer'}>
<div>{titlePrompt}</div>
</div>
<br/>
<ImageList cols={4}>
{customers.map(customer => (
<ImageListItem key={customer.name}>
<img alt={customer.name} src={`data:image/png;base64,${logos[customer.name]}`}></img>
</ImageListItem>
))}
</ImageList>
</div>
</div>
);
};
export default Customers;
What I’m trying to do is fetch customer information such as name and logo and display them in an ImageList.
The problem is, first I have to fetch the name and token (used for subsequent API calls) for each customer from one database, then I need to fetch the logos one by one since they all reside in specific customer databases. I want to render the component with only the img component’s alt text and then populate the images as they come in. I just can’t figure out how to synchronize this correctly inside useEffect. I also don’t know if using the if statements to control what gets executed during subsequent useEffect calls is the right thing to do or just hacky.
I tried chaining .then but the setState calls are asynchronous so it leads to race conditions and doesn’t work.
Thank you for reading.