4

I want to wait for 10 seconds for my API call to fetch the category list array from backend and store in hook state. If nothing is fetched in 10 sec, I want to set error hook state to true.

But the problem is even after the array is fetched initially, the error state is set to true and categoriesList array in state blanks out after 10 sec.

import React, { useState, useEffect } from "react";

import { doGetAllCategories } from "../helper/adminapicall.js";

const ViewCategories = () => {
  let [values, setValues] = useState({
    categoriesList: "",
    error: false,
  });

  let { categoriesList, error } = values;

  const preloadCategories = () => {
    doGetAllCategories()
      .then((data) => {
        if (data.error) {
          return console.log("from preload call data - ", data.error);
        }
        setValues({ ...values, categoriesList: data.categories });
      })
      .catch((err) => {
        console.log("from preload - ", err);
      });
  };

  useEffect(() => {
    preloadCategories();

    let timerFunc = setTimeout(() => {
      if (!categoriesList && !error) {
        setValues({
          ...values,
          error: "Error fetching category list... try after some time !",
        });
      }
    }, 10000);

    return () => {
      clearTimeout(timerFunc);
    };
  }, []);



//...further code

2 Answers 2

9

The problem is that the useEffect callback is a closure over categoriesList, so you'll always see the initial categories list inside the callback, and you won't see any changes to it. Now one could add categoriesList as a dependency to the useEffect hook, that way the hook will be recreated on every categoriesList change, and thus you can see the changed version:

useEffect(/*...*/, [categoriesList]);

Now the good thing is, that by updating the hook the timeout also gets canceled, so if the category list is set, we just don't have to create a new timeout:

  useEffect(() => {
    if(!categoriesList && !error) {
      let timerFunc = setTimeout(() => {
        setValues({
          ...values,
          error: "Error fetching category list... try after some time !",
        });
      }, 10000);

      return () => clearTimeout(timerFunc);
  }
}, [!categoriesList, !error]); // some minor optimization, changes to the list don't bother this hook

I recommend you to read this blog post on useEffect by Dan Abramov.

Sign up to request clarification or add additional context in comments.

1 Comment

Genius 👌 Have been looking everywhere and this is what I needed. Thanks mate, you saved my day!
-1

The problem with your code is that you expect to have a change of the state of the component inside the useEffect hook. Instead, you create two variables inside the useEffect that track whether the limit of 10 sec has passed or the data is fetched. In contrary to state variables, you can expect these variables to change because they lie within the same useEffect.

export default function App() {
  const [data, setData] = React.useState(null);
  const [error, setError] = React.useState(null);
  React.useEffect(() => {
    let didCancel = false;
    let finished = false;
    async function fetchData() {
      const data = await subscribeAPI();
      if (!didCancel) {
        finished = true;
        setData(data);
      }
    }
    const id = setTimeout(() => {
      didCancel = true;
      if (!finished) {
        setError("Errorrrr");
      }
    }, 10000);

    fetchData();

    return () => {
      clearTimeout(id);
    };
  }, []);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.