如何在React Native中为API请求创建进度栏?

问题描述:

我试图制作一个简单的进度计数器,从0%到100%,而我的程序获取天气数据。 API请求由index.ios.js内的getLocation()制成,其在weatherApi.js内部调用fetchWeather()。有没有一种方法来衡量我的API请求取得功能的进展?如果没有,那么实现加载栏的好方法是什么?如何在React Native中为API请求创建进度栏?

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) 
+1

在浏览器中有onProgress事件。不确定在react-native中如何与node.js一起工作。也许你应该尝试另一个XHR库。 – abeikverdi

+1

查看[axios](https://www.npmjs.com/package/axios)以及[library](https://www.npmjs.com/package/axios-status) – abeikverdi

+1

@LaurentL,我明白了你总是说天气很好! ;) –

fetch API不包括any progress callbacks,所以你的选择是要么使用XMLHttpRequest,这是完全支持在React Native中,或者是基本上将要在其之上构建的库。例如,你可以修改你fetchWeather函数来完成:

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}); 
     } 
    } 
}); 
} 

进步在哪里是你更新状态的回调。例如,在你的组件,添加功能:

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 
    } 
} 

然后,呼叫变为:

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

的例子是从MDN调整。

+0

哇!这比我预料的更好的答案!非常感谢您花时间研究我的问题,并实际编写代码来演示它。 – LaurentL

+0

很高兴!让我们知道如果你遇到任何问题实施它:) – martinarroyo