How can I create a progress bar for an API request in React Native?

12,398

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.

Share:
12,398

Related videos on Youtube

LaurentL
Author by

LaurentL

Updated on September 14, 2022

Comments

  • LaurentL
    LaurentL over 1 year

    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)
    
    • abeikverdi
      abeikverdi over 7 years
      There is onProgress event in browsers. Not sure how that works with node.js in react-native. Maybe you should try another XHR library.
    • abeikverdi
      abeikverdi over 7 years
      Check out axios and also this library
    • yadhu
      yadhu over 7 years
      @LaurentL, I see you are always saying something good about the weather! ;)
  • LaurentL
    LaurentL over 7 years
    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.
  • martinarroyo
    martinarroyo over 7 years
    Gladly! Let us know if you run into any problems implementing it :)