Jacob Voyles
Jacob Voyles

Reputation: 13

Infinite Loop when making ajax call in React?

I am trying to get a list of movies from an array that I have. I am looping over the array to put the title and poster on the screen. But even though the loop only happens once, it keeps making the same three api calls over and over again. I am really new to React and thought that it should only make a new call if something changes?

import React from 'react';
import $ from 'jquery';

var movieArray = ['Tarzan','1o1uyry29ddz', 'tt0120855',
'Close Encounters Of The Third Kind', 'g3hl6ewv9b7h', 'tt0075860',
'10,000 BC', 'tngmycvr418q', 'tt0443649'
];

export class Home extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: '',
      poster: '',
      backdrop: '',
      genre: '',
      release: '',
      description: ''
    };
  }
componentDidUpdate(id){
    let self = this;
    let url = 'https://api.themoviedb.org/3/find/'
    let apiKey = '?api_key=<my-api-key>'
    let language = '&language=en-US'
    let imdb = '&external_source=imdb_id'
    $.ajax({
          type: 'GET',
          url: url + id + apiKey + language + imdb
    })
    .done(function(data) {
        self.setState({
          name: data.title,
          poster: data.poster_path,
          backdrop: data.backdrop_path,
          genre: data.genre_ids,
          release: data.release_data,
          description: data.overview
        });
      })
    .fail(function(jqXhr) {
      console.log('Failed to Connect to TMDB Server');
    });
}
movieLoad(){
    var arrayLength = movieArray.length;
    var movieList = [];
    var url = "http://image.tmdb.org/t/p/w185//"+this.poster ;
    for (var i = 0; i < arrayLength-2; i++) {
      this.componentDidUpdate(movieArray[i+2]);
      console.log(this.title);
      movieList.push(
        <div>
          <h1>{this.title}</h1>
          <img src= {url} />
        </div>
      );
      i = i + 2;
    }
    return movieList;
  }

  render() {
    return (
      <div>
        {this.movieLoad()}
      </div>
    );
  }
}

export default Home;

Upvotes: 0

Views: 3198

Answers (3)

Kryten
Kryten

Reputation: 15780

The problem here is the way you're calling movieLoad in your render function - each time it renders, it's going to call movieLoad, which calls componentDidUpdate, which will update the component state via setState, which will cause the component to re-render, which will call movieLoad, .... you can see where this is going.

The key is to update your state independently of the render method. React provides a couple of ways to do that:

  1. in response to events - for example, if you have a button to click, an event is fired that calls an onClick event handler where your state is updated. Then the render method is called by React to update the component.

  2. when the component is first loaded - in the componentDidMount method. You can call setState here and React will call render when it's ready

  3. when the component receives new properties from a parent - in the componentWillReceiveProps method. Then you can update the state with the new props via setState and render will be called by React

For a detailed look at these, check out the State and Lifecycle section of the React docs.

The key is that when you are updating the component state with the setState method, you let React take care of the rendering and don't try to change the state while it's rendering.

Upvotes: 1

suchcodemuchwow
suchcodemuchwow

Reputation: 1056

You have to write separate asynchronous function to call Ajax requests. It's very critical mistake to call lifecycle methods(componentDidUpdate etc.) on purpose. Just extract the Ajax call to another function and change your state by calling

this.setState({
          name: data.title,
          poster: data.poster_path,
          backdrop: data.backdrop_path,
          genre: data.genre_ids,
          release: data.release_data,
          description: data.overview
});

If it's still confusing to you take a look at Ajax Request's in React

Edited: I have assumed that you are going request multiple times. If you need to call your request only one time, you are free to use it in componentDidMount() but without calling it recursively or calling it from elsewhere again.

Upvotes: 1

Chaim Friedman
Chaim Friedman

Reputation: 6261

your issue is with this code right here.

componentDidUpdate(id){
    let self = this;
    let url = 'https://api.themoviedb.org/3/find/'
    let apiKey = '?api_key=7d7a79eb6ca4f02c996a7f8f0795293e'
    let language = '&language=en-US'
    let imdb = '&external_source=imdb_id'
    $.ajax({
          type: 'GET',
          url: url + id + apiKey + language + imdb
    })
    .done(function(data) {
        self.setState({
          name: data.title,
          poster: data.poster_path,
          backdrop: data.backdrop_path,
          genre: data.genre_ids,
          release: data.release_data,
          description: data.overview
        });
      })
    .fail(function(jqXhr) {
      console.log('Failed to Connect to TMDB Server');
    });

Every time you cal setState componentDidUpdate will fire again, and since you are calling setState inside of didUpdate, you are getting an infinite loop.

You have 2 options to fix this. First, you can move your ajax request to the didMount lifecycle method which only fires one time after the component has mounted. The other option, and this is assuming you have a reason to use didupdate, would be to have some check whether anything has in fact changed, and only call a setState when there is a reason to.

For most common use cases, option 1 is the way to go.

Upvotes: 0

Related Questions