I'm facing an issue in my app which is my api get called before I update my state (which I know that updating state is an async
act so I used then
) but this is not working.
the way app works is at first I get my categories from an api
then I call second api
based on selected categories (if none selected I show all products but if some selected I show products based on them). so here's my implementation:
const [CompanyCategories, setCompanyCategories] = useState({ loading: true, categories: [] });
const [companyProducts, setCompanyProducts] = useState([]);
const fetchCategories = useCallback(() => {
return fetch(`***Categories API***`)
.then(res => {
if (!res.ok) {
throw new Error('Failed to fetch.');
}
return res.json()
})
.then(data => {
setCompanyCategories({
loading: false, categories: data.map((el, index) => {
return {
id: el.id,
title: el.title,
selected: false
}
})
})
})
.catch(err => {
console.log(err);
});
}, []);
const fetchCompanyProducts = (companyId, selectedCats) => {
axios
.post('***Products API***', {
"companyID": companyId,
"groupOfCategory": selectedCats
})
.then(response => {
// response.data is the products
const products = response.data
console.log('this is products' ,products)
})
.catch(error => {
// error.message is the error message
console.log(error.message)
})
}
const fetchProductsHandler = () => {
const selectedCategoriesId = CompanyCategories.categories.filter(el => el.selected).map(el => el.id);
console.log('selected : ' , CompanyCategories.categories) // its empty array [] but it has to be [7,8,10,11] these are selected categories IDs which for none selected I'll send all as selected to backend.
if (selectedCategoriesId.length == 0) {
fetchCompanyProducts(companyId, CompanyCategories.categories.map((el, index) => el.id))
} else {
fetchCompanyProducts(companyId, selectedCategoriesId)
}
}
const onRadioBtnClick = (item) => {
let updatedState = CompanyCategories.categories.map((el) => {
if (el.id == item.id) {
return {
...el,
selected: !el.selected
}
} else {
return {
...el,
selected: el.selected
}
}
});
setCompanyCategories({ loading: false, categories: updatedState })
};
useEffect(() => {
fetchCategories()
.then(() => { //here I use then so after categories state updated, I call products
fetchProductsHandler()
})
}, [])
what I'm facing is I get categories successfully but second API called before my state get updated so the second argument of the products function is an empty array which is not supposed to be. so how can I solve this?
if i understand your question right:
you want when the categories are settled you fetch the products, you can achieve this easily using useEffect hook, and waiting for the categories value to change
useEffect(() => {
fetchCategories()
}, []);
useEffect(() => {
if(!CompanyCategories.loading) fetchProductsHandler()
}, [CompanyCategories]);