11

I'm trying to make a simple progress counter that goes from 0% to 100% while my program fetches weather data. The API request is made by getLocation() inside of index.ios.js which calls fetchWeather() inside of weatherApi.js. Is there a way to measure the progress of my API request made by the fetch function? If not, what would be a good way to implement a loading bar?

weatherAPI.js

const rootUrl ='http://api.openweathermap.org/data/2.5/weather?appid=fcea54d0ceade8f08ab838e55bc3f3c0'

export const fetchWeather = (lat,lon) => {

    const url = rootUrl+'&lat='+lat+'&lon='+lon+"&units=metric"
    console.log(url)

  return fetch(url)
    .then(res => res.json())
    .then(json => ({
        temp: json.main.temp,
        weather: json.weather[0].main
    }))

}

index.ios.js

import React, {Component} from 'react';
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    StatusBar
    } from 'react-native'

import Icon from 'react-native-vector-icons/Ionicons'
import {fetchWeather} from './weatherAPI'
import Highlight from 'react-native-highlight-words'

const iconNames = {
    Default: 'md-time',
    Clear: 'md-sunny',
    Rain: 'md-rainy',
    Thunderstorm: 'md-thunderstorm',
    Clouds: 'md-cloudy',
    Snow: 'md-snow', 
    Drizzle: 'md-umbrella',
}

const phrases = {

    Default:{
        title: "Fetchin the Weather",
        subtitle: "Be patient, you're witnessing a miracle",
        highlight: ["Fetchin"],
        color: "#636363",
        background: "#9C9C9C"
    },

    Clear: {
        title: "CLEAR.",
        subtitle: "You Better Go Outside",
        highlight: ["CLEAR"],
        color:"#E32500",
        background: "#FFD017"
    },
    Rain: {
        title: "It's Raining",
        subtitle: "You guessed it",
        highlight: ["Raining"],
        color:"#004A96",
        background:"#2F343A"
    },
    Thunderstorm: {
        title: "Not Just Raining, It's Storming",
        subtitle: "Free shower",
        highlight: ["Storming"],
        color:"#FBFF46",
        background:"#020202"
    },
    Clouds: {
        title: "Clouds for Days",
        subtitle: "Cotton candy skies",
        highlight: ["Days"],
        color:"#0044FF",
        background: "#939393"

    },
    Snow: {
        title: "Oh Yeah Bud. It's Snowin'",
        subtitle: "Make a snow angel bud",
        highlight: ["Snowin'"],
        color:"#021D4C",
        background:"#15A678"

    },
    Drizzle: {
        title: "Just a Wee Ol' Drizzle Lads",
        subtitle: "Free shower",
        highlight: ["Wee", "Ol'"],
        color:"#dbdbdb",
        background:"#1FBB68"

    },
}

class App extends Component {


  componentWillMount() {

    this.state = {

        temp: 0,
        weather: 'Default'
    }   

  }

  componentDidMount() {
    this.getLocation()
}

  getLocation() {
    navigator.geolocation.getCurrentPosition(
      posData => fetchWeather(posData.coords.latitude,posData.coords.longitude)
      .then(res => this.setState({
        temp:Math.round(res.temp),
        weather: res.weather
      })),
      error => alert(error),
      {timeout: 10000}
      )
  }


    render(){
        console.log(this.state.weather)
        return(
        <View style={[styles.container, {backgroundColor: phrases[this.state.weather].background}]}>
          <StatusBar hidden={true}/>
            <View style={styles.header}>
            <Icon name={iconNames[this.state.weather]} size={80} color={'white'}/>
            <Text style={styles.temp}>{this.state.temp}°</Text>
            </View>
            <View style={styles.body}>
            <Highlight 
              style={styles.title}
              highlightStyle={{color: phrases[this.state.weather].color}}
              searchWords={phrases[this.state.weather].highlight}
              textToHighlight={phrases[this.state.weather].title}
              />
            <Text style={styles.subtitle}>{phrases[this.state.weather].subtitle}</Text>
            </View>
        </View>
        )
    }
}

const styles = StyleSheet.create({

    container: {
        flex:1,
        backgroundColor:'#FFD017'
    },


    header: {
        flexDirection:'row',
        alignItems:'center',
        justifyContent:'space-around',
        flex:1,

    },
    temp: {
        fontFamily: 'HelveticaNeue-Bold',
        fontSize: 45,
        color:'white'

    },

    body: {
        alignItems:'flex-start',
        justifyContent:'flex-end',
        flex:5,
        margin:10

    },

    title: {
        fontFamily: 'HelveticaNeue-Bold',
        fontSize: 90,
        color:'white',
        marginBottom:5

    },
    subtitle: {
        fontFamily: 'HelveticaNeue-Medium',
        fontSize: 16,
        color:'white'

    }



});

AppRegistry.registerComponent('IsItRaining', () => App)
4
  • 1
    There is onProgress event in browsers. Not sure how that works with node.js in react-native. Maybe you should try another XHR library. Commented Nov 28, 2016 at 6:02
  • 1
    Check out axios and also this library Commented Nov 28, 2016 at 6:07
  • 1
    @LaurentL, I see you are always saying something good about the weather! ;) Commented Nov 28, 2016 at 6:13
  • Awesome, Thank you guys for the help! I will check out these suggestions. @free-soul hahah glad you caught on ;) Commented Nov 28, 2016 at 7:04

1 Answer 1

17

The fetch API does not include any progress callbacks, so your options are either use XMLHttpRequest, which is fully supported in React Native, or a library that essentially is going to build atop of that. For example, you can modify your fetchWeather function to do:

export const fetchWeather = (lat,lon, progress) => {
return new Promise((resolve, reject) => {
    const url = rootUrl+'&lat='+lat+'&lon='+lon+"&units=metric"
    console.log(url)
    var oReq = new XMLHttpRequest();

    oReq.addEventListener("progress", progress);
    oReq.open('GET', url);
    oReq.send();
    oReq.onreadystatechange = function() {
        if (oReq.readyState == XMLHttpRequest.DONE) {
            let data = JSON.parse(oReq.responseText);
            resolve({temp: data.main.temp, weather: json.weather[0].main});
        }
    }
});
}

Where progress is a callback in which you update the state. For instance, in your component, add the function:

function updateProgress (oEvent) {
  if (oEvent.lengthComputable) {
    var progress = oEvent.loaded / oEvent.total;
    this.setState({progress})
  } else {
    // Unable to compute progress information since the total size is unknown
  }
}

Then, the call becomes:

fetchWeather(posData.coords.latitude,posData.coords.longitude, this.updateProgress.bind(this)) fetchWeather(posData.coords.latitude,posData.coords.longitude)

The example is adapted from MDN.

Sign up to request clarification or add additional context in comments.

2 Comments

Wow! This a better answer than I could have ever expected! Thank you so much for taking the time to look into my problem and actually write out code to demonstrate it.
Gladly! Let us know if you run into any problems implementing it :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.