Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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)
like image 934
LaurentL Avatar asked Nov 28 '16 04:11

LaurentL


1 Answers

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.

like image 124
martinarroyo Avatar answered Sep 21 '22 07:09

martinarroyo