1

I am trying to fetch some data from API and loop through the parsed response content and render them.

But I am getting a warning

webpackHotDevClient.js:138 ./src/App.js

Line 20:  Expected an assignment or function call and instead saw an expression  no-unused-expressions

Here's my App.js:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import './Api.css';

class App extends Component {

  render() {
    var request = new XMLHttpRequest();
    var listItems;
    request.open('GET', 'https://ghibliapi.herokuapp.com/films', true);
    request.onload = function () {

      // Begin accessing JSON data here
      var data = JSON.parse(this.response);

      if (request.status >= 200 && request.status < 400) 
      {
            listItems = data.forEach(movie => {
                <div className="card">
                    <h1>{movie.title}</h1>
                    <p>{movie.description}</p>
                </div>
            });
      } 
      else {
        console.log('error');
      }
    }

    request.send();

    return (
    <div className="App">
        <img src={logo} className="App-logo" alt="logo" />
        <div className="container">
            {listItems}
        </div>
    </div>
    );
  }
}

export default App;

When I try to console.log(listItems) after request.send() I get undefined.

I assume undefined issue could be because of asynchronous behavior. How can I render these elements?

1 Answer 1

2

render method is not a suitable place to make asynchronous requests. You should move fetching code in a suitable lifecycle method like componentDidMount, or use a function then call this from componentDidmount. Then, using the component state you need to put this fetched data in your state.

The warning is coming from the linter. You are using forEach and forEach does not return anything. This is why the warning says it expects an assignment or a function call there. Instead of forEach, you should use map for creating JSX elements.

For the undefined part, you guessed it right. Your request is asynchronous and your render method does not wait for the asynchronous request to finish its job, wants to render listItems immediately but there are no listItems yet :) This is why we are using React's one of the best parts: the state and lifecycle methods.

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

  componentDidMount() {
    this.getItems();
  }

  getItems = () => {
    const that = this;
    const request = new XMLHttpRequest();
    request.open("GET", "https://ghibliapi.herokuapp.com/films", true);
    request.onload = function get() {
      const data = JSON.parse(this.response);

      if (request.status >= 200 && request.status < 400) {
        that.setState({ movies: data });
      } else {
        console.log("error");
      }
    };

    request.send();
  };

  listItems = () =>
    this.state.movies.map(movie => (
      <div className="card">
        <h1>{movie.title}</h1>
        <p>{movie.description}</p>
      </div>
    ));

  render() {
    return (
      <div className="App">
        <div className="container">{this.listItems()}</div>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Sign up to request clarification or add additional context in comments.

5 Comments

You are welcome @Azima. I've updated my answer a little bit. I think it is more clear right now.
one more thing, why did you use componentDidMount instead of componentWillMount? since componentDidMount hook is called after render method only.
For two reasons. 1. render method doesn't wait for componentWillMount. So, there is no difference between using componentDidMount or it. 2. componentWillMount will be deprecated in a future release :) reactjs.org/docs/react-component.html#unsafe_componentwillmount
listItems = () => return this.state.movies.map(movie => ( <div className="card"> <h1>{movie.title}</h1> <p>{movie.description}</p> </div> )); It should be returned, otherwise it wont work.
@KarthikRajan, it is an arrow function and has an implicit return. If you use return then you should use a body block there. See for more.

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.