0
votes

I'm having issue fetching data and setting them to state in a functional component using useEffect and useState.

My problem is that I would like to keep the data fetching done with axios async/await in a separate file for improving application scalability but then I don't understand how to update the state in case the promise is resolved (not rejected).

In particular I'm trying to retrieve from the promise an array of table rows called data in state, but I can't figure out how to set the result of the responce in the state

Here's the code in the component file:

const [data, setData] = React.useState([]);
useEffect(() => {
   const { id } = props.match.params;
   props.getTableRows(id).then((res) => {
     setData(res);
   });
   //or is it better:
   //props.getTableRows(id).then(setData);  ?
}, []);

and my action.js:

export const getTableRows = (id, history) => async (dispatch) => {
  try {
    const res = await axios.get(`/api/test/${id}`);
    dispatch({
      type: GET_TEST,
      payload: res.data.rows,
    });
 } catch (error) {
    history.push("/test");
 }
};

enter image description here In the above picture it can be seen that the rows array inside the promise response called in action.js is present.

This code unfortunately doesn't work, error: Uncaught (in promise) TypeError: Cannot read property 'forEach' of undefined

I've found out another solution which is the define the promise in the useEffect method like this:

useEffect(() => {
   const { id } = props.match.params;
   const fetchData = async () => {
       const result = await axios.get(`/api/test/${id}`);
       setData(result.data.rows);
   };

fetchData();
}, []);

this code is working in my app but as I said I don't like having the promises in the components files I would like instead to have them all the promise in action.js for app scalability (in case url change I don't have to change all files) but in that case I don't know where to put the setData(result.data.rows); which seems the right choise in this last example

Any suggestions? Thanks

2
You still need to use async/await. The .then() is executed when the value is returned, however your function will continue rendering and won't wait for it. (causing it to error our by trying to access forEach on a null state) - Greg M

2 Answers

0
votes

You still need to use async/await. The .then() is executed when the value is returned, however your function will continue rendering and won't wait for it. (causing it to error our by trying to access forEach on a null state). After it errors the promise via .then() will update the values and that is why you can see them in the console.

useEffect(() => {
  async function getData() {
      const { id } = props.match.params;
      await props.getTableRows(id).then((res) => {
       setData(res);
      });
  }
  getData()
}, []);

Additionally, before you access a state you can check for null values (good practice in general).

if (this.state.somestate != null) {
//Run code using this.state.somestate
}
0
votes

I don't see you return anything from getTableRows. You just dispatch the result, but hadn't return the res for the function call. And it will be helpful if you provided error trace.