在ReactNative中,使用fetch实现网络请求。fetch同XMLHttpRequest非常类似,是一个封装程度更高的网络API,使用起来很简洁,因为使用了Promise。
Promise是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理、更强大。ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成)、Rejected(已失败)。Promise实例生成以后,可以分别指定“完成”和“失败”状态的回调函数。实现方式:链式调用方法,fetch中使用的就是该特性。
语法:
fetch(参数)
.then(完成的回调函数)
.catch(失败的回调函数)
fetch(url, opts)
.then((response) => {
// 网络请求成功执行的回调函数,得到响应对象,通过response可以获取请求的数据。例如:json、text等等
return response.text();
// return response.json();
})
.then((responseData) => {
// 处理请求得到的数据
})
.catch((error) => {
// 网络请求失败执行该回调函数,得到错误信息
})
在POST请求中需要用到一个FormData的概念。
FormData
Web应用中频繁使用的一项功能就是表单数据的序列化,XMLHttpRequest2级定义了FormData类型,FormData主要用于实现序列化表单以及创建与表单格式相同的数据。
var data = new FormData();
data.append("name", "xiaoming");
append方法接收两个参数:键和值。分别对应表单字段的名字和字段中包含的值,添加多个键值对。
在jQuery中,“key1=value1&key2=value2”作为参数传入对象框架会自动封装成FormData形式。在Fetch中进行post请求时,需要自动创建FormData对象传给body。
示例1 - 网络请求的用法
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity
} from 'react-native';
// GET请求
function getRequest(url) {
var opts = {
method: "GET"
};
fetch(url, opts)
.then((response) => {
return response.text(); // 返回一个带文本的对象
})
.then((responseText) => {
alert(responseText);
})
.catch((error) => {
alert(error);
})
}
// POST请求
function postRequest(url) {
// 将“key1=value1&key2=value2”封装成FormData形式
let formData = new FormData();
formData.append("username", "xiaoming");
formData.append("password", "123");
var opts = {
method: "POST",
body: formData
};
fetch(url, opts)
.then((response) => {
return response.text(); // 返回一个带文本的对象
})
.then((responseText) => {
alert(responseText);
})
.catch((error) => {
alert(error);
})
}
var GetData = React.createClass({
render: function () {
return(
<View style={styles.container}>
<TouchableOpacity onPress={getRequest.bind(this, "http://demo.php?username=小&password=123")}>
<View style={styles.btn}>
<Text>GET</Text>
</View>
</TouchableOpacity>
<TouchableOpacity opPress={postRequest.bind(this, "http://demo.php")}>
<View style={styles.btn}>
<Text>POST</Text>
</View>
</TouchableOpacity>
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 30,
backgroundColor: "cyan",
flexDirection: "row",
justifyContent: "space-around",
alignItems: "center"
},
btn: {
width: 60,
height: 30,
borderWidth: 1,
borderRadius: 3,
borderColor: "black",
backgroundColor: "yellow",
justifyContent: "center",
alignItems: "center"
},
});
module.exports = GetData;
运行结果:
示例2 - 网络请求获取列表数据
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
ListView,
Image
} from 'react-native';
/*
展示电影列表
逻辑:未获取数据时,显示等待页面;获取数据时,显示电影列表页面
需要给state添加一个属性,用于记录数据获取状态
*/
var REQUEST_URL = "https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json";
var MovieList = React.createClass({
// 设置初始状态值
getInitialState: function () {
// 定义一个dataSource对象
var ds = new ListView.DataSource({
rowHasChanged: (oldRow, newRow) => oldRow != newRow
});
return {
// 数据是否下载成功的标识
loaded: false,
dataSource: ds
};
},
// 请求数据
getData: function () {
fetch(REQUEST_URL)
.then((response) => {
return response.json();
})
.then((responseData) => {
// 刷新组件,重新渲染组件,展示ListView
// 得到新的数据,更新dataSource
this.setState({
loaded: true,
dataSource: this.state.dataSource.cloneWithRows(responseData.movies)
});
})
.catch((error) => {
alert(error);
})
},
render: function () {
// 如果未请求到数据,提示“加载等待”页面
if (!this.state.loaded) {
return this.renderLoadingView();
}
// 电影列表
return (
<ListView
style={styles.listView}
dataSource={this.state.dataSource}
initialListSize={10}
renderHeader={this._renderHeader}
renderRow={this._renderRow}
renderSeparator={this._renderSeparator}
/>
);
},
// 组件挂载完成 生命周期函数
componentDidMount: function () {
// 组件挂载后,开始请求数据
this.getData();
},
// 等待加载页面
renderLoadingView: function () {
return(
<View style={styles.loadingContainer}>
<Text style={styles.loadingText}>Loading movies ...</Text>
</View>
);
},
// 渲染行
_renderRow: function (movie) {
return (
<View style={styles.rowContainer}>
<Image style={styles.thumbnail} source={{uri:movie.posters.thumbnail}}/>
<View style={styles.textContainer}>
<Text style={styles.title}>{movie.title}</Text>
<Text style={styles.year}>{movie.year}</Text>
</View>
</View>
);
},
// 渲染头部
_renderHeader: function () {
return (
<View style={styles.header}>
<Text style={styles.header_text}>Movie List</Text>
<View style={styles.headerSeparator}></View>
</View>
);
},
// 渲染分割线
_renderSeparator: function (sectionID: number, rowID: number) {
var style = {
height: 1,
backgroundColor: "#CCC"
};
return (
<View style={style} key={sectionID + rowID}></View>
);
}
});
var styles = StyleSheet.create({
// Loading样式
loadingContainer: {
flex: 1,
marginTop: 25,
backgroundColor: "cyan",
justifyContent: "center",
alignItems: "center",
},
loadingText: {
fontSize: 30,
fontWeight: "bold",
textAlign: "center",
marginLeft: 10,
marginRight: 10
},
// ListView Row样式
rowContainer: {
flexDirection: "row",
padding: 5,
alignItems: "center",
backgroundColor: "#F5FCFF"
},
thumbnail: {
width: 53,
height: 81,
backgroundColor: "gray"
},
textContainer: {
flex: 1,
marginLeft: 10
},
title: {
marginTop: 3,
fontSize: 18,
textAlign: "center"
},
year: {
marginTop: 3,
marginBottom: 3,
textAlign: "center"
},
// List Header样式
header: {
height: 44,
backgroundColor: "#F5FCFF"
},
header_text: {
flex: 1,
fontSize: 20,
fontWeight: "bold",
textAlign: "center"
},
headerSeparator: {
height: 1,
backgroundColor: "#CCC"
},
listView: {
marginTop: 25,
backgroundColor: "#F5FCFF"
}
});
module.exports = MovieList;
运行结果