Cannot read property 'code' of undefined from promise, context api (react hook)

I am making my first hybrid web app using react hook.
I am faced with a timing problem with promise and context api.

Here are the logic I am facing with.

  1. A function fetchApplications gets data from firebase firestore, it is defined in globalcontext.js (called in tableView)
  2. tableView.js calls fetchApplications in useEffect.
  3. fetchApplications is defined as promise function, I expect it will resolve(return) data until data fetched, it will resolove(return) object like {code:200, data:data}

problem
in the fetchData.code,

Cannot read property ‘code’ of undefined

Here is my code

In the tableview.js

  React.useEffect(() => {
      context.fetchApplications(auth.userId, "")
      .then(function (fetchData) {
      console.log("Fetched data: ", fetchData);   ///this is undefined
      if (fetchData.code !== 200) {               /// error part
        alert(fetchData.msg);
      }
      if (!fetchData.data) {
        alert("No applications");
      }
      setsData(fetchData.data);
    });
  }, []);

In the GlobalContext.js

async function fetchApplications(userId, role) {
    return new Promise((resolve, reject) => {
      // resolve({ code: 200, data: "data" });     //If I add this code, it will be alright.

      let dataArray = [];
      let applicationRef = db
        .collection("Users")
        .doc(userId)
        .collection("Applications");
      applicationRef
        .get()
        .then(function (qs) {
          qs.forEach(function (doc) {             //doesn't work
            console.log(doc.id, " => ", doc.data());
            console.log(doc.size, " => ", typeof doc);
            dataArray.push(doc.data());
          });
          return Promise.resolve(dataArray);
        })
        .then((data) => {
          console.log("Global, Fetched Data", dataArray);
          if (data) {
            resolve({ code: 200, data: data });
          }
          return;
        });
    }).catch(function (error) {
      reject({ code: 400, msg: "시스템에러 고객센터에 문의해주세요" });
    });
  }

wrote in codesendbox
If I was write the wrong way of promise, please let me know.
thanks

27 thoughts on “Cannot read property 'code' of undefined from promise, context api (react hook)”

  1. You’re implementing a couple of bad practices and have some major issues. For starters, fetchApplications is marked as async but you’re returning a manually created promise which is quite overkill as your fetching code actually generates a promise which you should directly return. Furthermore:

    .then(function (qs) {
      qs.forEach(function (doc) {             //doesn't work
        console.log(doc.id, " => ", doc.data());
        console.log(doc.size, " => ", typeof doc);
        dataArray.push(doc.data());
      });
      return Promise.resolve(dataArray);
    })
    

    I am not sure what exactly "//doesn’t work" should mean but return Promise.resolve(dataArray); won’t cut it for you. You’re already in the then chain, so you can’t resolve anything from the main promise at this point. You should just pass the data to the next then callback as return dataArray;.

    All in all, I will suggest ditching the then-ables syntax all together and migrate to async/await altogether:

    async function fetchApplications(userId, role) {
       try {
          const dataArray = [];
          const applicationRef = db
             .collection("Users")
             .doc(userId)
             .collection("Applications");
    
          const querySnapshot = await applicationRef.get();
    
          querySnapshot.forEach(doc => {
             dataArray.push(doc.data());
          });
    
          return {
             code: 200,
             data: dataArray
          };
       }
       catch (error) {
          return {
             code: 400,
             msg: '시스템에러 고객센터에 문의해주세요'
          };
       }
    }
    

    Then, in your react component/hook:

    React.useEffect(() => {
       const fetchApplicationDataAsync = async () => {
          const result = await context.fetchApplications(auth.userId, "");
    
          if (result.code !== 200) {
             alert(fetchData.msg);
          }
          if (!fetchData.data) {
             alert("No applications");
          }
    
          setsData(fetchData.data);
       }
    
       fetchApplicationDataAsync();
    }, [auth.userId]);
    

    Another problem and bad practice is that you’re not specifying your dependencies for the useEffect hook. You have 2 external dependencies: the auth.userId paramater and the ontext.fetchApplications function. We alleviate one of the problem by creating the fetch function in the body of useEffect itself. However, the auth.userId should go into the dependency array as stated above.

    Reply
  2. You have to check for fetchData to be defined before accessing its properties.

    A short form would be

    if (fetchData && fetchData.code !== 200){...}
    

    Applied to your code:

     React.useEffect(() => {
          context.fetchApplications(auth.userId, "")
          .then(function (fetchData) {
          console.log("Fetched data: ", fetchData);   ///this is undefined
          if (fetchData && fetchData.code !== 200) {               /// error part
            alert(fetchData.msg);
          }else {
            alert("No applications");
          }
          setsData(fetchData.data);
        });
      }, []);
    
    Reply
  3. By calling then() on the fetchApplications() function, as follows, you pass to the callback the fullfilment value from the Promise returned by fetchApplications() (i.e. fetchData gets the value returned by fetchApplications()).

      context.fetchApplications(auth.userId, "")
      .then(function (fetchData) {...}
    

    However, fetchApplications() returns a Promise that resolves with undefined because, actually, you don’t return the Promises chain. This is why you get an error on fetchData.code.

    Adapting fetchApplications() as follows (using await, since you use async) should do the trick (untested!):

      async function fetchApplications(userId, role) {
        try {
          let dataArray = [];
          let applicationRef = db
            .collection('Users')
            .doc(userId)
            .collection('Applications');
    
          const qs = await applicationRef.get();
          qs.forEach(doc => {
            console.log(doc.id, ' => ', doc.data());
            console.log(doc.size, ' => ', typeof doc);
            dataArray.push(doc.data());
          });
          return { code: 200, data: dataArray };
        } catch (error) {
          return { code: 400, msg: '시스템에러 고객센터에 문의해주세요' };
        }
      }
    

    Note that in any case you return an object with a code property, so no more problem when doing fetchData.code.

    Reply

Leave a Comment