Samy
Samy

Reputation: 1033

How store data from fetch

I'm pretty new in React and need some help. I wanted display data from a movie database based on the search term. I'm using fetch inside my getMovies methode to get the data. The data is stored in data.Search but I don't know how to access it and store it in a variable.

class DataService
{
    getMovies (searchTerm) {
        //let dataStorage; //store somehow data.Search inside

        fetch("http://www.omdbapi.com/?s=" + searchTerm, {
           method: 'get'
        })
        .then((resp) => resp.json())
        .then(function(data) {
            return data.Search;
        }).catch(function(error) {
            console.log(error);// Error :(
        });
    }
    //return dataStorage; //return data.Search
}

Upvotes: 0

Views: 1516

Answers (2)

thinhvo0108
thinhvo0108

Reputation: 2232

The below code is the correct react's way for your case, as simple as this:

import React from 'react';

export default class DataService extends React.Component {

    constructor(props) {
      super(props);
      this.state = {
        search_data: [], //for storing videos
      };
      this.getMovies = this.getMovies.bind(this); //bind this function to the current React Component
    }

    getMovies (searchTerm) {
        fetch("http://www.omdbapi.com/?s=" + searchTerm, {
           method: 'get'
        })
        .then((resp) => resp.json())
        .then((data) => { //so that this callback function is bound to this React Component by itself
            // Set state to bind search results into "search_data"
            // or you can set "dataStorage = data.Search" here
            // however, fetch is asynchronous, so using state is the correct way, it will update your view automatically if you render like I do below (in the render method)
            this.setState({
              search_data: data.Search,
            });
        });
    }

    componentDidMount() {
      this.getMovies(); //start the fetch function here after elements appeared on page already
    }

    render() {
       return (
         {this.state.search_data.map((video, i) =>{
            console.log(video);
            // please use the correct key for your video below, I just assume it has a title
            return(
              <div>{video.title}</div>
            )
          })}         
       );
    }
}

Feel free to post here any errors you may have, thanks

Upvotes: 1

Przemysław Zalewski
Przemysław Zalewski

Reputation: 3986

There are several ways of doing asynchronous tasks with React. The most basic one is to use setState and launch a Promise in the event handler. This might be viable for basic tasks but later on, you will encounter race conditions and other nasty stuff.

In order to do so, your service should return a Promise of results. On the React side when the query changes, the service is called to fetch new results. While doing so, there are few state transitions: setting loading flag in order to notify the user that the task is pending and when the promise resolves or rejects the data or an error is stored in the component. All you need is setState method.

More advanced techniques are based on Redux with redux-thunk or redux-saga middlewares. You may also consider RxJS - it is created especially for that kind of stuff providing debouncing, cancellation and other features out of the box.

Please see the following example of simple search view using yours DataService.

class DataService
{
  //returns a Promise of search results, let the consumer handle errors
  getMovies (searchTerm) {
    return fetch("http://www.omdbapi.com/?s=" + searchTerm, {
      method: 'get'
    })
    .then((resp) => resp.json())
    .then(function(data) {
      return data.Search;
    })
  }
}

const SearchResults = ({data, loading, error}) => 
<div>
  {
    data && data.map(({Title, Year, imdbID, Type, Poster}) => 
      <div key={imdbID}>
        {Title} - {Year} - {Type}
        <img src={Poster} />
      </div>
    )
  }
  {loading && <div>Loading...</div>}
  {error && <div>Error {error.message}</div>}
</div>

class SearchExample extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this)
    this.state = {
      data: [],
      loading: false
    };
  }

  handleChange(event) {
    const service = new DataService();
      
    //search query is target's value
    const promise = service.getMovies(event.target.value);
      	
    //show that search is being performed
    this.setState({
      loading: true
    })
        
    //after the promise is resolved display the data or the error
    promise.then(results => {
      this.setState({
        loading: false,
        data: results
      })
    })
    .catch(error => {
      this.setState({
        loading: false,
        error: error
      })
    })
  }

  render() {
    return (
      <div>
        <input placeholder="Search..." onChange={this.handleChange} type="search" />
        <SearchResults data={this.state.data} loading={this.state.loading} error={this.state.error} />
      </div>
    )
  }
}

ReactDOM.render(<SearchExample />, document.getElementById("app"));
<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="app"></div>

Upvotes: 1

Related Questions