0

I am trying to call an API from React but it keeps returning as undefined. This is my code:

import React from 'react';

export default class UserList extends React.Component {
    constructor(props) {
        super(props);
        this.state = { customer: [] };
    }

    componentDidMount() {
        fetch('https://recommenderapi.herokuapp.com/customer_id=x', {
            mode: 'no-cors',
            method: "post",
            headers: {
                "Content-Type": "application/json"
            }
        }).then(({ results }) => this.setState({ customer: results }));
    }

    render() {
        console.log(this.state.customer)
        const customers = this.state.customer.map((item, i) => (
            <div>
                <h1>{item.customer_id}</h1>
                <h1>{item.prediction}</h1>
            </div>
        ));

        return (
            <div id="layout-content" className="layout-content-wrapper">
                <div className="panel-list">{customers}</div>
            </div>
        );
    }
}

This is what the API looks like: enter image description here

When I run this, the console.log() returns undefined which then means I get the error TypeError: Cannot read property 'map' of undefined. I am not too sure how to fix this so any help would be appreciated! Thanks.

EDIT: This is what the API returns when you go to the link: {"prediction":["Roam in Rome",""]}

ajnabz
  • 297
  • 5
  • 25
  • You need to default the customer to empty array until you get the response from the server, after the data comes from the server, setState will re-render the view with your data. – Nicolae Maties Apr 06 '21 at 09:33
  • Clearly, the response you're getting doesn't have a `results` property. We can't help you because we don't know what that API returns. If it just returns an array, don't destructure what you get back, use it directly. Fundamentally, look in the network tab to see what you get back and update your fulfillment handler accordingly. – T.J. Crowder Apr 06 '21 at 09:34
  • 1
    @NicolaeMaties - They are, *that* part is right. `this.state = { customer: [] };` – T.J. Crowder Apr 06 '21 at 09:34
  • I think you should do it like this `https://recommenderapi.herokuapp.com?customer_id=x`. – angelo Apr 06 '21 at 09:36
  • 1
    It's not the problem, but that `fetch` code is falling prey to the `fetch` API footgun I describe [here](http://blog.niftysnippets.org/2018/06/common-fetch-errors.html): You need to check for HTTP success/failure by looking at the `ok` flag. Then, probably, as [Sarpang PM](https://stackoverflow.com/a/66966133/157247) says, you need to use `json()` to read the response and convert the JSON you're sending back (if you're sending back JSON). – T.J. Crowder Apr 06 '21 at 09:37
  • @T.J.Crowder didn't noticed that, you are right, he's missing a `await response.json()` for fetch. – Nicolae Maties Apr 06 '21 at 09:40
  • *"EDIT: This is what the API returns when you go to the link:"* Please post code, error messages, markup, etc. **as text**, not as a *picture* of text. Why: http://meta.stackoverflow.com/q/285551/157247 I don't see anything called `results` in that response. I see `prediction`, not `results`. – T.J. Crowder Apr 06 '21 at 09:58

2 Answers2

3

You aren't reading the body of the response and parsing the JSON; instead, you're trying to use a result property on the Response object, which doesn't have one.

To read the body of the response you need to use the methods json(), text(), blob(), and similar. You're probably sending back JSON, so you'd want json(). You also need to check the ok flag first, since if the request fails with a 404 or 500 HTTP error, fetch will fulfill the promise but the result may not be JSON. Finally, you should handle/report errors from fetch:

fetch('https://recommenderapi.herokuapp.com/customer_id=x', {
    mode: 'no-cors',
    method: "post",
    headers: {
        "Content-Type": "application/json"
    }
})
.then(response => {
    // *** Check for HTTP success
    if (!response.ok) {
        throw new Error(`HTTP error ${response.status}`);
    }
    // *** Read the body, parse it as JSON
    return response.json();
})
.then(({ results }) => this.setState({ customer: results }))
.catch(error => {
    // ...*** handle/report error...
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Sarang PM
  • 398
  • 2
  • 12
  • 1
    Not **into** JSON, **from** JSON. But this is likely the answer, since the OP is clearly trying to use `{result}` on the `Response` object, instead of on the body of the response. – T.J. Crowder Apr 06 '21 at 09:35
  • When I do this it just returns an empty array. Do you know why this could be happening? – ajnabz Apr 06 '21 at 09:48
  • @ajnabz - If the response is what you say it is, it's not even returning an empty array. – T.J. Crowder Apr 06 '21 at 09:58
  • 1
    @T.J.Crowder I followed your other message regarding cors and it is now returning ```(2) ["Rome", ""]0: "Rome"1: ""length: 2__proto__: Array(0)``` so thank you. – ajnabz Apr 06 '21 at 10:15
  • @ajnabz - Excellent! I'm glad you got that sorted out! :-) – T.J. Crowder Apr 06 '21 at 10:21
0

I think it has to to with mode: "no-cors" that you've specified. MDN documentation says it prevents application/json headers from being sent. Try to change it to "cors"

  • Hi, I kept getting the error ```Access to fetch at '...' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.``` which is why I added the no-cors – ajnabz Apr 06 '21 at 09:47
  • @ajnabz - `no-cors` doesn't magically make the data available to you on a cross-origin `fetch`. See: https://stackoverflow.com/questions/20035101/why-does-my-javascript-code-receive-a-no-access-control-allow-origin-header-i – T.J. Crowder Apr 06 '21 at 10:00
  • That doesn't solve your problem. https://stackoverflow.com/questions/36292537/what-is-an-opaque-response-and-what-purpose-does-it-serve here is the answer. You can only access cross-origin resources once those resources allow your host in their headers, that's what CORS is for. – john doe Apr 06 '21 at 10:01
  • 1
    @T.J.Crowder Ah i see, this is for a group coursework and the other person told me there was nothing they could do about the error and so I just followed this answer https://stackoverflow.com/questions/61238680/access-to-fetch-at-from-origin-http-localhost3000-has-been-blocked-by-cors. I will look into the link though thank you. – ajnabz Apr 06 '21 at 10:09
  • @ajnabz - Good luck! I hope you can resolve it. Happy coding. :-) – T.J. Crowder Apr 06 '21 at 10:16