0

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

1
  • 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) Commented Aug 14, 2020 at 15:01

2 Answers 2

0

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
}
Sign up to request clarification or add additional context in comments.

4 Comments

added async /await as in your solution but I still get the error: Uncaught TypeError: Cannot read property 'forEach' of undefined
and plus a warning : react-dom.development.js:88 Warning: An effect function must not return anything besides a function, which is used for clean-up. It looks like you wrote useEffect(async () => ...) or returned a Promise. Instead, write the async function inside your effect and call it immediately: useEffect(() => { async function fetchData() { // You can await here const response = await MyAPI.getData(someId); // ... } fetchData(); }, [someId]); // Or [] if effect doesn't need props or state
Updated my answer to fix the warning (apologies). Try using the null check before doing Data.forEach. In the return you can use {Data != null ? (<div></div>) : null } to only render it if valid otherwise nothing.
Perhaps you could just have let data = await props.getTableRows(id) and setData(data)
0

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.

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.