AndrewB
AndrewB

Reputation: 35

React setState fetch API

I am starting to learn React and creating my second project at the moment. I am trying to usi MovieDb API to create a movie search app. Everything is fine when I get the initial list of movies. But onClick on each of the list items I want to show the details of each movie. I have created a few apps like this using vanilla JS and traditional XHR call. This time I am using fetch API which seems straightforward ans simply to use, however when I map through response data to get id of each movie in order to retrieve details separately for each of them I get the full list of details for all the items, which is not the desired effect. I put the list of objects into an array, because after setState in map I was only getting the details for the last element. I know that I am probably doing something wrong within the API call but it might as well be my whole REACT code. I would appreciate any help.

My code

App.js

import React, { Component } from 'react';
import SearchInput from './Components/SearchInput'
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);

      this.state = 
        {
          value: '',
          showComponent: false, 
          results: [],
          images: {},
        };

    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleOnChange = this.handleOnChange.bind(this);
    this.getImages = this.getImages.bind(this);
    this.getData = this.getData.bind(this);
}

ComponentWillMount() {
  this.getImages();
  this.getData();
}

getImages(d) {
  let request = 'https://api.themoviedb.org/3/configuration?api_key=70790634913a5fad270423eb23e97259'
  fetch(request)
    .then((response) => {
        return response.json();
    }).then((data) => {
      this.setState({
        images: data.images
      });
    });
}

  getData() {
    let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.state.value+'');

    fetch(request)
      .then((response) => {
        return response.json();
      }).then((data) => {               
        this.setState({
          results: data.results
        });             
      });
  }

  handleOnChange(e) {
    this.setState({value: e.target.value})
  }

  handleSubmit(e) {
    e.preventDefault();
    this.getImages();
    this.setState({showComponent: true});
    this.getData();
  }

  render() {
    return (
      <SearchInput handleSubmit={this.handleSubmit} handleOnChange={this.handleOnChange} results={this.state.results} images={this.state.images} value={this.state.value} showComponent={this.state.showComponent}/>
    );
  }
}

export default App;


SearchInput.js

import React, {Component} from 'react';
import MoviesList from './MoviesList';

class SearchInput extends Component {
  render() {
    return(
      <div className='container'>
        <form id='search-form' onSubmit={this.props.handleSubmit}>
          <input value={this.props.value} onChange={this.props.handleOnChange} type='text' placeholder='Search movies, tv shows...' name='search-field' id='search-field' />
          <button type='submit'>Search</button>
        </form>
        <ul>
          {this.props.showComponent ?
              <MoviesList value={this.props.value} results={this.props.results} images={this.props.images}/> : null
          }
        </ul>
      </div>
    )
  }
}

export default SearchInput;

This is the component where I try to fetch details data

MovieList.js

import React, { Component } from 'react';
import MovieDetails from './MovieDetails';

let details = [];

class MoviesList extends Component {
  constructor(props) {
    super(props);

    this.state = {
      showComponent: false,
      details: []
    }

    this.showDetails = this.showDetails.bind(this);
    this.getDetails = this.getDetails.bind(this);

  }


  componentDidMount() {
    this.getDetails();
  }

  getDetails() {
    let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');

    fetch(request)
      .then((response) => {
        return response.json();
      }).then((data) => {    
      data.results.forEach((result, i) => {
        let url = 'https://api.themoviedb.org/3/movie/'+ result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
        return fetch(url)
        .then((response) => {
          return response.json();
        }).then((data) => {
          details.push(data)   
          this.setState({details: details});   
        });
      });   
      console.log(details);
  });
}



  showDetails(id) {
    this.setState({showComponent: true}, () => {
      console.log(this.state.details)
    });
    console.log(this.props.results)
  }

  render() {
    let results;
    let images = this.props.images;

    results = this.props.results.map((result, index) => {
      return(
        <li ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
          {result.title}{result.id}
          <img  src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
        </li>
      )
    });

    return (
      <div>
        {results}
        <div>
          {this.state.showComponent ? <MovieDetails details={this.state.details} results={this.props.results}/> : null}
        </div>
      </div>
    )
  }
}

export default MoviesList;

MovieDetails.js

import React, { Component } from 'react';

class MovieDetails extends Component {


  render() {
    let details;
    details = this.props.details.map((detail,index) => {
      if (this.props.results[index].id === detail.id) {
      return(
        <div key={detail.id}>
          {this.props.results[index].id}  {detail.id}
        </div>
      )} else {
        console.log('err')
      }
    });

    return(
      <ul>
        {details}
      </ul>
    )

  }
}

export default MovieDetails;

Upvotes: 3

Views: 4156

Answers (2)

AndrewB
AndrewB

Reputation: 35

Thanks for all the answers but I have actually maanged to sort it out with a bit of help from a friend. In my MovieList I returned a new Component called Movie for each component and there I make a call to API fro movie details using each of the movie details from my map function in MovieList component

Movielist

import React, { Component } from 'react';
import Movie from './Movie';

class MoviesList extends Component {
  render() {
    let results;
    if(this.props.results) {
      results = this.props.results.map((result, index) => {
        return(
          <Movie key={result.id} result={result} images={this.props.images}/>
        )
      });
    }

    return (
      <div>
        {results}
      </div>
    )
  }
}

export default MoviesList;

Movie.js

import React, { Component } from 'react';
import MovieDetails from './MovieDetails';

class Movie extends Component {
  constructor(props) {
    super(props);

    this.state = {
      showComponent: false,
      details: []
    }

    this.showDetails = this.showDetails.bind(this);
    this.getDetails = this.getDetails.bind(this);
  }

  componentDidMount() {
    this.getDetails();
  }

  getDetails() {
    let request = new Request('https://api.themoviedb.org/3/search/movie?api_key=70790634913a5fad270423eb23e97259&query='+this.props.value+'');

    fetch(request)
      .then((response) => {
        return response.json();
      }).then((data) => {    
        let url = 'https://api.themoviedb.org/3/movie/'+ this.props.result.id +'?api_key=70790634913a5fad270423eb23e97259&append_to_response=videos,images';
        return fetch(url)
      }).then((response) => {
          return response.json();
      }).then((data) => { 
          this.setState({details: data});   
      });
}

  showDetails(id) {
    this.setState({showComponent: true}, () => {
      console.log(this.state.details)
    });
  }

  render() {
    return(
      <li ref={this.props.result.id} id={this.props.result.id} key={this.props.result.id} onClick={this.showDetails}>
        {this.props.result.title}
        <img  src={this.props.images.base_url +`${this.props.images.poster_sizes?this.props.images.poster_sizes[0]: 'err'}` + this.props.result.backdrop_path} alt=''/>
        {this.state.showComponent ? <MovieDetails details={this.state.details}/> : null}
      </li>
    )

  }
}


export default Movie;

Upvotes: 0

Gavin Thomas
Gavin Thomas

Reputation: 1867

Theres a lot going on here...

//Here you would attach an onclick listener and would fire your "get details about this specific movie function" sending through either, the id, or full result if you wish.

//Then you getDetails, would need to take an argument, (the id) which you could use to fetch one movie.

getDetails(id){
fetch(id)
displayresults, profit
}

results = this.props.results.map((result, index) => {
          return(
            <li onClick={() => this.getDetails(result.id) ref={result.id} id={result.id} key={result.id} onClick={this.showDetails}>
              {result.title}{result.id}
              <img  src={images.base_url +`${images.poster_sizes?images.poster_sizes[0]: 'err'}` + result.backdrop_path} alt=''/>
            </li>
          )
        });

Upvotes: 1

Related Questions