sindhugauri
sindhugauri

Reputation: 189

React .map giving errors for rendering JSON received from fetch call

I'm learning React and am using the fetch API to send a get request to an HTTP server. I'm able to successfully retrieve the JSON. The code for that is as follows :-

componentDidMount() {
                console.log("Send GET request to Go server")
                fetch("http://192.168.121.106:8080/site", {
                        method: "GET",
                        headers: {
                                Accept: "application/json",
                                "Content-Type": "application/json",
                                'Access-Control-Allow-Origin': '*',
                        }
                }).then(response => {
                        if(response.status === 200) {
                                response.text().then(data => {
                                        this.setState({
                                                siteData : data
                                        });

                                });
                        }
                })
        }

I want to render this JSON in the form of a table. siteData is an object. This is what I have in my render function :-

render() {

            const siteVar =  JSON.stringify(this.state.siteData);

            return (
                    <React.Fragment>
                            <table className="table">
                                    <thead>
                                            <tr>
                                                    <th >S. NO.</th>
                                                    <th >NAME</th>
                                                    <th >AGE</th>
                                            </tr>
                                    </thead>
                                    <tbody>

                                            { (this.state.siteData).map(site =>
                                                    <tr>
                                                            <td> {site.name} </td>
                                                            <td> {site.age} </td>
                                                    </tr>

                                            )}
                                    </tbody>
                            </table>
                    </React.Fragment>
            )
    }
}

With this code, I'm getting an error that:

this.state.siteData.map is not a function

What am I doing wrong here?

This is a sample of what the JSON will look like :-

{
  "1": {
    "name": "Alice",
    "age": "23",
  },
  "2": {
    "name": "Bob",
    "age": "25",
  }
}

Upvotes: 0

Views: 176

Answers (1)

goto
goto

Reputation: 4425

The reason why you're getting an error is because you're trying to use the map method on an object. You need to turn your object into an array to be able to use map and store that in your state instead.

const response = {
  "1": {
    "name": "Alice",
    "age": "23",
  },
  "2": {
    "name": "Bob",
    "age": "25",
  }
}

const responseAsArray = Object.keys(response)
  .map(key => {
    return {
      siteNum: key,
      ...response[key]
    }
  })

console.log(responseAsArray)

Going back to your code, it'd look like the following:

componentDidMount() {
  fetch(...)
    .then(response => {
      // Note the use of `.json()` here
      // if your API returns some JSON as a response
      // use this instead of `text()` since it will
      // parse a JSON response into native JavaScript objects
      if (response.status === 200) {
        return response.json()
      }
      // TODO: handle errors appropriately
    })
    .then(json => {
      const siteData = Object.keys(json).map(key => {
        return { siteNum: key, ...json[key] }
      })
      this.setState({ siteData })
    })
    .catch(/* TODO: handle errors appropriately */)
}

Here's a working example:

class App extends React.Component {
  state = { siteData: [] };

  componentDidMount() {
    fakeFetch()
      .then(response => {
        if (response.status === 200) {
          return response.json();
        }
      })
      .then(data => {
        const siteData = Object.keys(data).map(key => {
          return { siteNum: key, ...data[key] };
        });
        this.setState({ siteData });
      })
      .catch(console.error);
  }

  render() {
    const { siteData } = this.state;

    if (siteData.length === 0) {
      return "Loading";
    }
    return (
      <table>
        <thead>
          <tr>
            <th>S. NO.</th>
            <th>NAME</th>
            <th>AGE</th>
          </tr>
        </thead>
        <tbody>
          {siteData.map(({ siteNum, name, age }) => (
            <tr key={siteNum}>
              <td>{siteNum}</td>
              <td>{name}</td>
              <td>{age}</td>
            </tr>
          ))}
        </tbody>
      </table>
    );
  }
}

// NOTE:
// This assumes your API returns a JSON response
// that looks like like this: 
// {"1":{"name":"Alice","age":"23"},"2":{"name":"Bob","age":"25"}}

function fakeFetch() {
  return Promise.resolve({
    status: 200,
    json: () => {
      return {
        "1": {
          name: "Alice",
          age: "23"
        },
        "2": {
          name: "Bob",
          age: "25"
        }
      };
    }
  });
}

ReactDOM.render(
  <App />,
  document.getElementById("app")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="app"></div>

Upvotes: 2

Related Questions