0

I'm attempting to set a global variable equal to a JSON element being returned from a Promise with Axios, within my React Native application. I've followed the advice from this question, but still am unable to set the variable's value.

Here is my method using an Axios call:

  temp_high = null;

  _getForecast(zipcode)
  {
    const request = "http://api.wunderground.com/api/" + API_KEY + "/forecast/q/" + zipcode + ".json";
    return axios.get(request).then( (response) => {
        if(response.status == 200) {
            this.response = response.data;
            return this.response;
        }
    });
  }

And my render:

render() {
this._getForecast(49306).then(data => {
  this.temp_high = parseInt(data.forecast.simpleforecast.forecastday[0].high.fahrenheit);
});
return (
  <View style={styles.container}>
    <Text>Weather for Belmont, MI</Text>
    <Text>High: {this.temp_high}</Text>
    <Text></Text>
  </View>
  );
 }
}

If I log data.forecast.simpleforecast.forecastday[0].high.fahrenheit to the console, or create an alert calling that, the value is indeed correct. I just cannot seem to set it equal to temp_high.

Ben Parsell
  • 75
  • 1
  • 12
  • Your async operation won't finish before the render. Can't do it this way. I don't know react myself, but it probably has a different way to incorporate async operations in a render operation. – jfriend00 Mar 20 '18 at 02:42
  • What is an adequate solution? Can I add a waiting point somewhere to ensure that it does finish? – Ben Parsell Mar 20 '18 at 02:44
  • See [Reactjs async rendering of components](https://stackoverflow.com/questions/27192621/reactjs-async-rendering-of-components) – jfriend00 Mar 20 '18 at 02:45
  • Also, [How to render components after successful async call](https://hashnode.com/post/reactjs-how-to-render-components-only-after-successful-asynchronous-call-ciwvnkjr400sq7t533lvrpdtw). – jfriend00 Mar 20 '18 at 02:47

3 Answers3

3
  1. If you want your component's view to update in response to the new data, you need to use setState, to tell React to re-render. React doesn't react (hue) to regular class properties.

  2. Async functions shouldn't be called from render. You should instead use lifecycle hooks, and componentDidMount would work best for this situation, to fetch the information once on mount.

With that in mind, you'd end up with something like this:

class Example extends React.Component {
  state = {
    data: null
  }

  componentDidMount() {
    // fetch forecast data when the component mounts
    this._getForecast(49306)
  }

  _getForecast(zipcode) {
    const request =
      "http://api.wunderground.com/api/" +
      API_KEY +
      "/forecast/q/" +
      zipcode +
      ".json"

    return axios.get(request).then(response => {
      if (response.status == 200) {
        this.setState({ data: response.data })
      }
    })
  }

  render() {
    if (this.state.data === null) {
      // don't render if we haven't received data yet
      // otherwise we'll get an error trying to calculate temp_high
      return
    }

    const temp_high = Number(
      this.state.data.forecast.simpleforecast.forecastday[0].high.fahrenheit
    )
    return (
      <View style={styles.container}>
        <Text>Weather for Belmont, MI</Text>
        <Text>High: {temp_high}</Text>
        <Text />
      </View>
    )
  }
}
kingdaro
  • 11,528
  • 3
  • 34
  • 38
  • 1
    Using state seemed to fix that. Thank you! React Native is brand new to me — something they don't seem to teach in Uni. Cheers! – Ben Parsell Mar 20 '18 at 03:03
1

If you want to assign to a standalone variable (even if it happens to be in the global scope), don't prefix it with this. Just

temp_high = Number(data.forecast.simpleforecast.forecastday[0].high.fahrenheit);

this resolves to the calling context of the current function you're in.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
-1

This answer is wrong, the arrow allows for https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) be carefull with the keyword "this". It is currently setting the this.temp_high to the _getForecast function. What you might want to do is have

 render() {
var self = this;
 this._getForecast(49306).then(data => {
 self.temp_high 
=parseInt(data.forecast.simpleforecast.forecastday[0].high.fahrenheit);
});
return (
  <View style={styles.container}>
    <Text>Weather for Belmont, MI</Text>
    <Text>High: {this.temp_high}</Text>
    <Text></Text>
  </View>
  );
 }
}
Luc-Olsthoorn
  • 186
  • 2
  • 13