3

I've just started a simple weather-app project to train React and data fetching.

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

import axios from 'axios';


class App extends Component {

  constructor(props) {
    super(props);

    this.state = {
      city: "",
      id: 0
    }

    this.choseCity = this.choseCity.bind(this);
    this.cityName = this.cityName.bind(this);
    this.cityInfo = this.cityInfo.bind(this);
  }


  chooseCity(e) {
    console.log(e.target.value)
    this.setState({
      city: e.target.value
    });
  }


  cityName() {
    axios.get(`https://www.metaweather.com/api/location/search/?query=${this.state.city}`)
      .then(res => res.data)
      .then(data => this.setState({
        city: data[0].title,
        id: data[0].woeid}))
  }

  cityInfo() {
    axios.get(`https://www.metaweather.com/api/location/${this.state.id}/`)
      .then(res => console.log(res.data))
  }


  render() {
    return (
      <div className="App">
        <input type="text" placeholder="Enter city name" value={this.state.city} onChange={this.chooseCity}/>
        <button onClick={this.cityName}>Name</button>
        <button onClick={this.cityInfo}>Info</button>

      </div>
    );
  }
}

export default App;

So, I have 2 functions (cityName and cityInfo) that I'm able to execute on 2 different onClick events. They both seem to work independently.

  • cityName() requests data that is stored in my state.

  • cityInfo() uses this state in the url of the request to get further informations.

I'm trying to chain them to be able to retrieve all the data in one call, but since they're both async, my second request starts before the data from the first one is stored in my state, and there is no way in the api I can directly get the info I need in one request.

I've tried a couple of things like grouping them in a single function, but nothing conclusive so far.

Solution: by @elsyr

this is how to chain in one function, using data between the requests:

cityInfo() {
    axios.get(`https://www.metaweather.com/api/location/search/?query=${this.state.city}`)
      .then(res => res.data)
      .then(data => {
        axios.get('https://www.metaweather.com/api/location/' + data[0].woeid)
          .then(res => res.data)
          .then (data => this.setState({
            info: data
          }))
      });
  }
kevscript
  • 55
  • 2
  • 6
  • Possible duplicate of [Axios: chaining multiple API requests](https://stackoverflow.com/questions/44182951/axios-chaining-multiple-api-requests) – jmargolisvt Dec 23 '17 at 22:35
  • tried with async/await, it still doesn't seem to store data in my state before making the second request :/ – kevscript Dec 23 '17 at 22:55

1 Answers1

1

From what I can tell, you want to trigger a call as in cityInfo right after the state is set in cityName.

This is entirely possible - setState is asynchronous (as it seems you've figured out), but setState also has an optional callback parameter that you can use. The callback is guaranteed to fire off after the state is modified.

Your code could look something like this in cityName:

cityName() {
    axios.get(`https://www.metaweather.com/api/location/search/?query=${this.state.city}`)
      .then(res => res.data)
      .then(data => this.setState({
        city: data[0].title,
        id: data[0].woeid}),
        this.cityInfo); // callback here! runs after the state is set!
 }

The other option you have is just to chain together your axios calls. If we look your chunk of code here:

cityName() {
    axios.get(`https://www.metaweather.com/api/location/search/?query=${this.state.city}`)
      .then(res => res.data)
      .then(data => {
          // data here already contains the id - data[0].woeid
          // we can just use it here to kick off another request - something like:
          // axios.get('https://www.metaweather.com/api/location/' + data[0].woeid)
      });
  }

Because you used .then(), you know that you're guaranteed to have your data before you start off your cityInfo call. This will probably take a bit more refactoring, but it's probably a better idea if you're only keeping the state from cityName to use it in cityInfo.

elsyr
  • 736
  • 3
  • 9
  • The second solution worked perfectly! I already tried to chain axios requests like that, but I wasnt using the data provided by the first request. Thanks! – kevscript Dec 23 '17 at 23:20