skip to Main Content

I have the following useEffect hook in a react component that fetches data from a api. http is an axios instance. I have a react use State hook const [inputsData, setInputsData] = useState(false);.

    useEffect(() => {
        if (active.id) {
            http.get(`/stock/${title}/${active.id}`).then((response) => {
                setInputsData(response.data);
                console.log(inputsData);
            });
        }
    }, [title, active.id, refresh]);

The data shown is to be refreshed and rerendered whenever the title (which is a URL Route Parameter), active.id (active is a react state that stores the data of the current item to be shown) or the refresh variable changes.

But, eventhough I have confirmed that the data coming from the api is correct, when I set the state of the component, when I console.log it, it shows the old state. The state being rendered is also the old one and I have to renavigate to the active item or refresh the page in order to see the correct data.

Basically:
I click on a, it shows the data for a.
I click on b, it shows the data for a.
I click on c, it shows the data for b.

Any idea on why the data is not being set correctly?

I tried running it twice to simulate renavigating to the active item. That didn’t work.
I tries setting the state to false and then setting it to the new data. That didn’t work.
I tried using setTimeout on the setState because I thought delaying it might help. That didn’t work.

Edit:
I removed useEffect. My code is now:

        if (active.id && !inputsData) {
            http.get(`/stock/${title}/${active.id}`).then((response) => {
                setInputsData(response.data);
                console.log(inputsData);
            });
        }

This replaces the useEffect.
inputsData is set to false on the button click and on the rerender, the new value for it is fetched from the api.
Thanks to user mmm for linking the article.

3

Answers


  1. Chosen as BEST ANSWER

    I removed useEffect. My code is now:

            if (active.id && !inputsData) {
                http.get(`/stock/${title}/${active.id}`).then((response) => {
                    setInputsData(response.data);
                    console.log(inputsData);
                });
            }
    

    This replaces the useEffect. inputsData is set to false on the button click and on the rerender, the new value for it is fetched from the api. Thanks to user mmm for linking the article.


  2. you are probably having a race condition where here is what happens:

    1- you have the current state

    2- you make a change that triggers the useEffect but with the old data (changing that refresh state probably?)

    3- you make the actual change you need, like changing the title or active.id

    4- since you had 2 requests, the one from 2 and the one from 3 and since these are asynchronous actions, the Promise from 3 might resolve before the Promise from 2, thus the state will use the last updated state which is from 2 in this case

    If this was the problem, you can solve that by using boolean flag
    so it will look like this:

        useEffect(() => {
            let ignore = false;
            if (active.id) {
                http.get(`/stock/${title}/${active.id}`).then((response) => {
                   if (!ignore) {
                    setInputsData(response.data);
                    console.log(inputsData);
                  }
                });
            }
        return () => {ignore = true}
        }, [title, active.id, refresh]);
    
    Login or Signup to reply.
  3. useState hook is asynchronous , so sometimes you will see old state data.
    for getting new data we can rewrite code like this
    setInputsData((oldState) => {…oldState, …response.data} );

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search