Home > database >  How to implement cleanup function in useEffect while fetching data from API in contex provider
How to implement cleanup function in useEffect while fetching data from API in contex provider

Time:12-31

I want to display a list of products based on specific categories fetched from api, like below:

const API = "https://dummyjson.com/products";

const ProductsList = () => {
  const { cate } = useParams(); //here I am getting category from Viewall component
  const { getFilterProducts, filter_products } = useFilterContext();
  useEffect(() => {
    getFilterProducts(`${API}/category/${cate}`);
  }, [cate]);
  return (
    <div className="mx-2 mt-2 mb-16 md:mb-0 grid grid-cols-1 md:grid-cols-12">
      <div className="h-9 w-full md:col-span-2">
        <FilterSection />
      </div>
      <div className="md:col-span-10">
        <ProductListDetails products={filter_products} />
      </div>
    </div>
  );
};

My FilterContextProvider is as follows

const initialState = {
  filter_products: [],
};

const FilterProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const { products } = useAppContext();
  const getFilterProducts = async (url) => {
    dispatch({ type: "FILTERS_LOADING" });
    try {
      const res = await fetch(url);
      const data = await res.json();
      if (!res.ok) {
        var error = new Error("Error"   res.status   res.statusText);
        throw error;
      }
      dispatch({ type: "LOAD_FILTER_PRODUCTS", payload: data.products });
    } catch (err) {
      dispatch({ type: "FILTERS_ERROR", payload: err.message });
    }
  };
  return (
    <FilterContext.Provider value={{ ...state, getFilterProducts }}>
      {children}
    </FilterContext.Provider>
  );
};

I tried using this simple approach in my ProductList component to clean up:

useEffect(() => {
  let inView = true;
  getFilterProducts(`${API}/category/${cate}`);

  return () => {
    inView = false;
  };
}, [cate]);

But it does not seem to work. When I move to the ProductList component, it first displays data of my previous filer_products value, then after a few fractions of seconds, updates the data and shows current data. I am expecting that when the ProductList component unmounts, its rendered data should vanish, and when I navigate it again, it should render the current data directly, not after a fraction of seconds.

CodePudding user response:

As you explained, I assume your context is wrapping your routes, and it's not re-rendering when switching between pages. A simple solution is to have a loader in ProductsList, wait for the new data to replace the old, and have the user notice what's happening with a loader:

const ProductsList = () => {
  const { cate } = useParams(); //here I am getting category from Viewall component
  const { getFilterProducts, filter_products } = useFilterContext();
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    getFilterProducts(`${API}/category/${cate}`).then(() => {
      setLoading(false);
    });
  }, [cate]);

  if (loading) {
    return <p>Hang tight, the data is being fetched...</p>;
  }
  return (
    <div className="mx-2 mt-2 mb-16 md:mb-0 grid grid-cols-1 md:grid-cols-12">
      <div className="h-9 w-full md:col-span-2">
        <FilterSection />
      </div>
      <div className="md:col-span-10">
        <ProductListDetails products={filter_products} />
      </div>
    </div>
  );
};

If you need to clear your store in a clean-up function, you can add dispatch as part of your context value, grab it in ProductsList and call it like so:

<FilterContext.Provider value={{ ...state, getFilterProducts, dispatch }}>
  {children}
</FilterContext.Provider>
const { getFilterProducts, filter_products, dispatch } = useFilterContext();
useEffect(() => {
  getFilterProducts(`${API}/category/${cate}`);

  return () => {
    dispatch({ type: "LOAD_FILTER_PRODUCTS", payload: {} });
  };
}, [cate]);
  • Related