Reputation: 3919
I'm working on a React app, which connects to a firebase database. Part of the app reads in a list of Items belonging to the current User, and then reads a config object in for each one, and finally updates the items in state. I'm having trouble getting it to work - I seem to keep getting
My component is:
const Dashboard = () => {
const authUser = useContext(AuthUserContext);
const firebase = useContext(FirebaseContext);
const [error, setError] = useState<string|null>(null);
const [firebaseToken, setFirebaseToken] = useState<string|null>(null);
const [items, setItems] = useState<Array<ItemModel>>([]);
// On first render, get all Items
useEffect(() => {
if(!authUser) return;
if(!firebase) return;
let userId = authUser.id;
const getItems = () => {
firebase.doGetIdToken()
.then((token) => {
// Save token so it can be passed down
setFirebaseToken(token);
url = "items/" + userId;
Client.getData(url, token)
.then((itemResults:Array<ItemModel>) => {
// Get config for each Item
// Set up an empty array to hold the new data
const itemResultsWithConfigs:Array<ItemModel> = []
// Now get the config for each item
itemResults.forEach((item:ItemModel) => {
// Get config for this Item
url = "/items/config/" + item.id;
Client.getData(url, token)
.then((newConfig:ConfigModel) => {
let newItem:ItemModel = {
...item,
config: newConfig
}
// Add full item to list & update list
itemResultsWithConfigs.push(newItem);
})
})
setItems(itemResultsWithConfigs);
})
});
})
})
.catch(() => setError("Unable to connect to database"))
}
getItems();
}, [authUser, firebase])
return (
<>
<ul>
{
items.map((item:ItemModel) => {
return <li key={item.id}>{item.name}</li>
})
}
</ul>
</>
);
}
export default Dashboard;
Client.getData is:
async function getData(path:string, token:string) {
const object:AxiosRequestConfig = {
...obj,
method: 'GET',
headers: {
...obj.headers,
'Authorization': `Bearer ${token}`,
},
};
try {
const response:AxiosResponse = await axios.get(`${baseUrl}${path}`, object);
checkStatus(response);
const parsedResult = parseJSON(response);
return parsedResult;
} catch (error) {
throw error;
}
}
The problem is that the async function (getData) is returning at different times, therefore the array of items is being overwritten some of the time. Currently this is only rendering one or two of the items instead of the 3 that I know should be there.
How do I approach this?
Upvotes: 0
Views: 605
Reputation: 281784
Since itemResultsWithConfig is derived asynchronously, a good idea is to map and wait for all the promises to resolve using Promise.all
const getItems = () => {
firebase.doGetIdToken()
.then((token) => {
// Save token so it can be passed down
setFirebaseToken(token);
url = "items/" + userId;
Client.getData(url, token)
.then((itemResults:Array<ItemModel>) => {
// Get config for each Item
// Set up an empty array to hold the new data
// Now get the config for each item
let promises = itemResults.map((item:ItemModel) => {
// Get config for this Item
url = "/items/config/" + item.id;
return Client.getData(url, token)
.then((newConfig:ConfigModel) => {
let newItem:ItemModel = {
...item,
config: newConfig
}
return newItem;
})
})
Promise.all(promises).then((itemResultsWithConfigs:Array<ItemModel>) => setItems(itemResultsWithConfigs))
})
});
})
Upvotes: 2