How can I create a progress bar for an API request in React Native?
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.
Related videos on Youtube
LaurentL
Updated on September 14, 2022Comments
-
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 ofindex.ios.js
which callsfetchWeather()
inside ofweatherApi.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 over 7 yearsThere is onProgress event in browsers. Not sure how that works with node.js in react-native. Maybe you should try another XHR library.
-
abeikverdi over 7 years
-
yadhu over 7 years@LaurentL, I see you are always saying something good about the weather! ;)
-
-
LaurentL over 7 yearsWow! 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 over 7 yearsGladly! Let us know if you run into any problems implementing it :)