Search code examples
reactjsreactn

Fetch from products API after fetching from Categories API done?


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?


Solution

  • 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]);