Component not updating after fetching new array

Viewed 436

I'm new to ReactJS and to JS in general.

I have a table that populates with data from an API request. Each row has a delete button that deletes that specific entry. When you press the delete button a Modal opens asking you to confirm the operation, if you click YES the DELETE request gets passed to the API, a new GET request gets asked to the API to retrieve the new updated list of elements, the Modal closes and you should see the new updated table.

Or at least, that's what should be happening.

Right now when I confirm the delete operation the Modal closes but the table doesn't update, however if I refresh the page the table shows the updated content without the deleted entry so everything seems to be working correctly except for the table not updating after confirmation.

This code works if I press F5 to manually refresh the page:

function TableCategory(prop, ref) {
  const [info, setInfo] = useState([]);

  useImperativeHandle(ref, () => {
    return {
      updateTable: () => {
        fetchData();
      },
    };
  });

  useEffect(() => {
    fetchData();
  }, []);

  const fetchData = async () => {
    const response = await getCategoryList(UserService.getUserToken());

    let newArr = [...response.data];
    setInfo(newArr);
  };


Now if I try to "simulate a refresh" running fetchData() again after a delay, it works correctly and the component updates after the Modal closes without the need to manually refresh the page. I assume I'm not handling the ASYNC behavior as I should.

This code works without the need to manually refresh:

useImperativeHandle(ref, () => {
    return {
      updateTable: () => {
        fetchData();
        setTimeout(() => {
          fetchData();
        }, 200);
      },
    };
  });

This is the async method performing the request to the API:

export function getCategoryList(myToken) {
  return Promise.resolve(axios.get(CATEGORY_ENDPOINT, config(myToken)));
}

If there is something else you need to see, please let me know I only posted what I thought was relevant to the question.

Thanks!

3 Answers

I think your problem is here:

 useEffect(() => {
    fetchData();
  }, []);

This empty array tolts react to refresh only once, so try to pass props to it.

    useEffect(() => {
    fetchData();
  }, [info]);

should work.

For closer looks: https://reactjs.org/docs/hooks-effect.html quite at the end over next steps.

So, I think I've figured it out.

Turns up I was handling Promises badly. I wasn't waiting for the DELETE request promise to resolve. I was requesting a new "info" that contained the same data as the one before the DELETE. The code was actually working as supposed to. That's why if I ran fetchData() again with a delay the table would update.

This link helped me understand what I was doing wrong.

https://masteringjs.io/tutorials/fundamentals/promise

I leave the working code below.

This function makes a DELETE request; when it resolves, it fires a GET request to update the data for the table { fetchData() } and closes the Modal:

const closeAndDelete = (id) => {
    deleteCategory(id, getUserToken()).then(() => {
      prop.updateTable();
      prop.closeModal();
    });
  };

This exposes the updateTable() function; and fetches data from the API; when that resolves it stores the data in the info state:

function TableCategory(prop, ref) {
  const [info, setInfo] = useState([]);

  useImperativeHandle(ref, () => {
    return {
      updateTable: () => {
        fetchData();
      },
    };
  });

  useEffect(() => {
    fetchData();
  }, []);

  const fetchData = async () => {
    await getCategoryList(UserService.getUserToken()).then((response) =>
      setInfo(response.data)
    );
  };

//MORE CODE INVOLING RENDERING THE TABLE AND HANDLING THE DATA. NOT RELEVANT TO THE QUESTION//

}

The effect hook runs when the component mounts but also when the component updates.

Because you have passed empty array [], the useEffect runs just once a time when the table is mounted

But you want the component updates and the effect runs again. That's why you can provide the info state a second argument to the effect hook to activating it on component updates and not only for the mounting of the component.

useEffect(() => {
    fetchData();
  }, [info]);
Related