skwidbreth
skwidbreth

Reputation: 8414

JavaScript/React - returning Promise inside a function

I'm having a little trouble dealing with some Promises in my app, any clarification would be much appreciated.

I've been building a Phoenix/React app loosely based on this tutorial - https://medium.com/@benhansen/lets-build-a-slack-clone-with-elixir-phoenix-and-react-part-3-frontend-authentication-373e0a713e9e - and I'm trying to restructure my code a bit to make it easier for me to build out other aspects of the app in the future.

Initially, when posting login data to my Phoenix server, the function that I was using looked like this (from Login.jsx):

fetch(`${apiUrl}/sessions`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({person: person})
}).then(response => {
   this.setState({loadingData: false}, () => {
     response.json().then(result => {
        if(result.status === "error"){
          this.setState({error: {isError: true, message: result.message}}, () => {
            return;
          })
        }
        else{
          this.login(result) //DO SOME OTHER STUFF WITH THE RESULT
        }
     })
   })
}).catch(error => {
   console.error("There was an error: " + error);
});

and this worked just fine.

However, I have since restructured my code so that the fetch functionality has been moved into another file. Here's how it looks now (somewhat similar to the tutorial):

fetch.js

let parseResponse = (response) => {
    return response.json().then((json) => {
        if (!response.ok){
            return Promise.reject(json)
        }
        return json;
    });
}

let fetchFunctions = {
    post: (url, data) => {
        const body = JSON.stringify(data)
        fetch(`${apiUrl}${url}`, {
            method: 'POST',
            headers: headers(),
            body: body
        })
        .then(parseResponse)
    }
}

export default fetchFunctions;

Login.jsx

post('/sessions', {person: person})
  .then((result) => {
      this.login(result) //HERE'S THAT LOGIN FUNCTION I WANT TO RUN
})

Now when I run this, you may not be surprised to learn that I get the error Uncaught TypeError: Cannot read property 'then' of undefined, and I get it, I think... please correct me if I'm wrong, but the reason that this doesn't work is because fetch() is a Promise, but I have now wrapped it inside of a function that is not a Promise.

If I add console.log(json) before the return statement in parseResponse(), I do see my data and it looks good... but how can I get that data out of the Promise and into my component? It seems to me that I need to defined post() as a Promise as well, but I'm not sure how to structure this.

Upvotes: 1

Views: 11028

Answers (1)

Felix Kling
Felix Kling

Reputation: 816432

but the reason that this doesn't work is because fetch() is a Promise, but I have now wrapped it inside of a function that is not a Promise.

Functions are not promises. Functions can return promises. You simply forgot to return the result of fetch, which is a promise, from post:

let fetchFunctions = {
    post: (url, data) => {
        const body = JSON.stringify(data)
        return fetch(`${apiUrl}${url}`, {
    //  ^^^^^^
            method: 'POST',
            headers: headers(),
            body: body
        })
        .then(parseResponse)
    }
}

Now post returns a promises as well.

If you don't return, the implicit return value will be undefined, hence the error message "Uncaught TypeError: Cannot read property 'then' of undefined"

Simplest repro case for this error:

function foo(){}

foo().then();

Upvotes: 7

Related Questions