前言


  • 本文有配套视频,可以酌情观看。
  • 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我。
  • 文中所有内容仅供学习交流之用,不可用于商业用途,如因此引起的相关法律法规责任,与我无关。
  • 如文中内容对您造成不便,烦请联系 277511806@qq.com 处理,谢谢。
  • 转载麻烦注明出处,谢谢。

属性声明和属性确定


  • 有朋友反馈这边 属性声明和属性确定 不了解,这边就来补充一下。

  • React-Native 创建的自定义组件是可以复用的,而开发过程中一个组件可能会由多个人同时开发或者多个人使用一个组件,为了让开发人员之间减少沟通成本,我们会对某些必要的属性进行属性声明,让使用的人知道需要传入什么!甚至有些需要传入但没有传入值的属性我们会进行警告处理!

  • 这边先来看下 属性声明 的示例:

	static propTypes = {
name:PropTypes.string,
ID:PropTypes.number.isRequired,
}
  • 上面我们声明了 nameID 两个属性,并且进行了属性的确认,其中,'isRequired' 表示如果不传递这个属性,那么开发阶段中,系统会出现警告,让我们对其进行属性确认,也就是说是否为必须属性。

  • 属性确认语法分为:

    • 属性为任何类型
    	React.PropTypes.any
    
    
    • 属性是否是 JavaScript 基本类型
    	React.PropTypes.array;
    React.PropTypes.func;
    React.PropTypes.bool;
    React.PropTypes.number;
    React.PropTypes.object;
    React.PropTypes.string;
    • 属性是某个 React 元素
    	React.PropTypes.element;
    
    
    • 属性为几个特定的值
    	React.PropTypes.oneOf(['value1', 'value2'])
    
    
    • 属性为指定类型中的一个
    	React.PropTypes.oneOfType([
    React.PropTypes.node,
    React.PropTypes.number,
    React.PropTypes.string
    ])
    • 属性为可渲染的节点
    	React.PropTypes.node;
    
    
    • 属性为某个指定类的实例
    	React.PropTypes.instanceOf(NameOfClass);
    
    
    • 属性为指定类型的数组
    	React.PropTypes.arrayOf(React.PropTypes.string)
    
    
    • 属性有一个指定的成员对象
    	React.PropTypes..objectOf(React.PropTypes.number)
    
    
    • 属性是一个指定构成方式的对象
    	React.PropTypes.shape({
    color:React.PropTypes.stirng,
    fontSize:React.PropTypes.number
    })
    • 属性默认值(当我们没有传递属性的时候使用)
    	static defaultProps = {
    name:'苍井空'
    };

占位图


  • 开发中,我们会有许多图片都是从网络进行请求的,但是,如果出现网络卡顿的情况,图片就会迟迟不出现,又或者有的并没有图片,这样图片就为空白状态;为了不让用户感觉太突兀影响用户体验,也为了视图整体性,一般我们会选择使用占位图先展示给用户看,等到图片加载完毕再将图片展示出来。

  • 这边我们需要对cell内部进行一些处理。

	 {/* 左边图片 */}
<Image source={{uri:this.props.image === '' ? 'defaullt_thumb_83x83' : this.props.image}} style={styles.imageStyle} />

无数据情况处理


  • 还是网络问题,在网络出现问题或者无法加载数据的时候,一般我们会展示空白页,在空白页中提示 无数据 之类的提示,比较好的还会使用 指示器 的方式告诉用户网络出现问题等等。

  • 这边我们做以下处理,当无数据时,我们就先初始化基础界面,然后展示 提示 页面,等到有数据时,再重新渲染数据。

  • 首先设置 无数据 页面

	import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
} from 'react-native'; export default class GDNoDataView extends Component { render() {
return(
<View style={styles.container}>
<Text style={styles.textStyle}>无数据 </Text>
</View>
); }
} const styles = StyleSheet.create({
container: {
flex:1,
justifyContent:'center',
alignItems:'center',
}, textStyle: {
fontSize:21,
color:'gray'
}
});
  • 接着,没有数据的时候我们进行一些处理就可以了
	// 根据网络状态决定是否渲染 listview
renderListView() {
if (this.state.loaded === false) {
return(
<NoDataView />
);
}else {
return(
<PullList
onPullRelease={(resolve) => this.fetchData(resolve)}
dataSource={this.state.dataSource}
renderRow={this.renderRow}
showsHorizontalScrollIndicator={false}
style={styles.listViewStyle}
initialListSize={5}
/>
);
}
}

listView 头部设置


  • 根据原版效果发现 提示标题 应该放到 ListView 的头部才对,所以这边就做下小修改。
	<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
showsHorizontalScrollIndicator={false}
style={styles.listViewStyle}
initialListSize={5}
renderHeader={this.renderHeader}
/>
  • renderHeader 方法实现
	// 返回 listview 头部
renderHeader() {
return (
<View style={styles.headerPromptStyle}>
<Text>根据每条折扣的点击进行统计,每5分钟更新一次</Text>
</View>
);
}

下拉刷新


  • 为了避免适配问题带来的麻烦,这边我们采用第三方框架 react-native-pull 实现下拉刷新和上拉加载更多的功能。
	<PullList
onPullRelease={(resolve) => this.fetchData(resolve)}
dataSource={this.state.dataSource}
renderRow={this.renderRow}
showsHorizontalScrollIndicator={false}
style={styles.listViewStyle}
initialListSize={5}
renderHeader={this.renderHeader}
/>
  • fetchData 方法修改
	// 网络请求
fetchData(resolve) {
setTimeout(() => {
fetch('http://guangdiu.com/api/gethots.php')
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.data),
loaded:true,
});
if (resolve !== undefined){
setTimeout(() => {
resolve(); // 关闭动画
}, 1000);
}
})
.done();
});
}

网络请求之POST(重要)


  • GETPOST 是我们请求 HTTP 接口常用的方式,针对表单提交的请求,我们通常采用 POST 的方式。

  • JQuery 中,传入对象框架会自动封装成 formData 的形式,但是在 fetch 中没有这个功能,所以我们需要自己初始化一个 FormData 直接传给 body (补充:FormData也可以传递字节流实现上传图片功能)。

	let formData = new FormData();
formData.append("参数", "值");
formData.append("参数", "值"); fetch(url, {
method:'POST,
headers:{},
body:formData,
}).then((response)=>{
if (response.ok) {
return response.json();
}
}).then((json)=>{
alert(JSON.stringify(json));
}).catch.((error)=>{
console.error(error);
})

首页模块


  • 这边我们按照前面提到的步骤,进行数据的加载。
	import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TouchableOpacity,
Image,
ListView,
Dimensions
} from 'react-native'; // 第三方
import {PullList} from 'react-native-pull'; const {width, height} = Dimensions.get('window'); // 引用外部文件
import CommunalNavBar from '../main/GDCommunalNavBar';
import CommunalHotCell from '../main/GDCommunalHotCell';
import HalfHourHot from './GDHalfHourHot';
import Search from './GDSearch'; export default class GDHome extends Component { // 构造
constructor(props) {
super(props);
// 初始状态
this.state = {
dataSource: new ListView.DataSource({rowHasChanged:(r1, r2) => r1 !== r2}),
loaded:true,
};
this.fetchData = this.fetchData.bind(this);
} // 网络请求
fetchData(resolve) {
let formData = new FormData();
formData.append("count", "30"); setTimeout(() => {
fetch('http://guangdiu.com/api/getlist.php', {
method:'POST',
headers:{},
body:formData,
})
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.data),
loaded:true,
});
if (resolve !== undefined){
setTimeout(() => {
resolve();
}, 1000);
}
})
.done();
});
} // 跳转到近半小时热门
pushToHalfHourHot() {
this.props.navigator.push({
component: HalfHourHot,
})
} // 跳转到搜索
pushToSearch() {
this.props.navigator.push({
component:Search,
})
} // 返回左边按钮
renderLeftItem() {
return(
<TouchableOpacity
onPress={() => {this.pushToHalfHourHot()}}
>
<Image source={{uri:'hot_icon_20x20'}} style={styles.navbarLeftItemStyle} />
</TouchableOpacity>
);
} // 返回中间按钮
renderTitleItem() {
return(
<TouchableOpacity>
<Image source={{uri:'navtitle_home_down_66x20'}} style={styles.navbarTitleItemStyle} />
</TouchableOpacity>
);
} // 返回右边按钮
renderRightItem() {
return(
<TouchableOpacity
onPress={()=>{this.pushToSearch()}}
>
<Image source={{uri:'search_icon_20x20'}} style={styles.navbarRightItemStyle} />
</TouchableOpacity>
);
} // 根据网络状态决定是否渲染 listview
renderListView() {
if (this.state.loaded === false) {
return(
<NoDataView />
);
}else {
return(
<PullList
onPullRelease={(resolve) => this.fetchData(resolve)}
dataSource={this.state.dataSource}
renderRow={this.renderRow}
showsHorizontalScrollIndicator={false}
style={styles.listViewStyle}
initialListSize={5}
renderHeader={this.renderHeader}
/>
);
}
} // 返回每一行cell的样式
renderRow(rowData) {
return(
<CommunalHotCell
image={rowData.image}
title={rowData.title}
/>
);
} componentDidMount() {
this.fetchData();
} render() {
return (
<View style={styles.container}>
{/* 导航栏样式 */}
<CommunalNavBar
leftItem = {() => this.renderLeftItem()}
titleItem = {() => this.renderTitleItem()}
rightItem = {() => this.renderRightItem()}
/> {/* 根据网络状态决定是否渲染 listview */}
{this.renderListView()}
</View>
);
}
} const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
backgroundColor: 'white',
}, navbarLeftItemStyle: {
width:20,
height:20,
marginLeft:15,
},
navbarTitleItemStyle: {
width:66,
height:20,
},
navbarRightItemStyle: {
width:20,
height:20,
marginRight:15,
}, listViewStyle: {
width:width,
},
});
  • OK,这边也已经成功拿到数据,所以接着就是完成 cell 样式部分就可以了。

效果:

navigator 跳转动画


  • 有时候我们需要在跳转的时候使用不同的跳转动画,比如我们 半小时热门 的跳转方式在 iOS 内叫 模态跳转,特性就是当页面退出后会直接销毁,多用于注册、登录等不需要常驻内存的界面。

  • react-native 中为了方便实现这样的功能,我们可以在初始化 Navigator 的时候,在 ‘configsSence’ 中进行操作;具体操作如下:


// 设置跳转动画
configureScene={(route) => this.setNavAnimationType(route)} // 设置Navigator跳转动画
setNavAnimationType(route) {
if (route.animationType) { // 有值
return route.animationType;
}else {
return Navigator.SceneConfigs.PushFromRight;
}
}
  • 这样我们在需要跳转的地方只需要传入相应的参数即可。

// 跳转到近半小时热门
pushToHalfHourHot() {
this.props.navigator.push({
component: HalfHourHot,
animationType:Navigator.SceneConfigs.FloatFromBottom
})
}

关闭 Navigator 返回手势

  • 上面操作后,发现这边有个小细节就是我们使用了 `` 作为跳转动画,但是当我们下拉的时候,动画中默认附带的 返回手势 会干扰我们 ListView 的滑动手势,这个怎么解决呢?其实很简单,我们只要关闭Navigator 手势就可以了嘛,怎么关闭呢?其实在源码中我们可以找到,手势包含在动画中,我们如果不需要,只需要给其赋值为 null ,这样它就不知道需要响应手势事件了,方法如下:

// 设置Navigator跳转动画
setNavAnimationType(route) {
if (route.animationType) { // 有值
let conf = route.animationType;
conf.gestures = null; // 关闭返回手势
return conf;
}else {
return Navigator.SceneConfigs.PushFromRight;
}
}

  • 这样我们就成功关闭了 Navigator 手势功能。

上拉加载更多


  • react-native-pull 框架的上拉加载使用也很简单,配合 onEndReachedonEndReachedThresholdrenderFooter 使用
	loadMore() {
// 数据加载操作
} renderFooter() {
return (
<View style={{height: 100}}>
<ActivityIndicator />
</View>
);
} // 根据网络状态决定是否渲染 listview
renderListView() {
if (this.state.loaded === false) {
return(
<NoDataView />
);
}else {
return(
<PullList
onPullRelease={(resolve) => this.fetchData(resolve)}
dataSource={this.state.dataSource}
renderRow={this.renderRow}
showsHorizontalScrollIndicator={false}
style={styles.listViewStyle}
initialListSize={5}
renderHeader={this.renderHeader}
onEndReached={this.loadMore}
onEndReachedThreshold={60}
renderFooter={this.renderFooter}
/>
);
}
}

网络请求基础封装


  • 到这里,相信各位对 React-Native 有所熟悉了吧,从现在开始我们要慢慢往实际的方向走,这边就先从网络请求这部分开始,在正式开发中,网络请求一般都单独作为一部分,我们在需要使用的地方只需要简单调用一下即可,这样做的好处是让整个 工程 的结构更加清晰,让组件们各司其职,只管好自己该管的事,并且后期维护成本也会相应降低。

  • 首先,我们要先对 fetchGETPOST 请求方式进行一层基础封装,也就是要把它们单独独立出来,那么这边先来看下 GET 这边:

	var HTTPBase = {};

	/**
*
* GET请求
*
* @param url
* @param params {}包装
* @param headers
*
* @return {Promise}
*
* */
HTTPBase.get = function (url, params, headers) {
if (params) { let paramsArray = []; // 获取 params 内所有的 key
let paramsKeyArray = Object.keys(params);
// 通过 forEach 方法拿到数组中每个元素,将元素与参数的值进行拼接处理,并且放入 paramsArray 中
paramsKeyArray.forEach(key => paramsArray.push(key + '=' + params[key])); // 网址拼接
if (url.search(/\?/) === -1) {
url += '?' + paramsArray.join('&');
}else {
url += paramsArray.join('&');
}
} return new Promise(function (resolve, reject) {
fetch(url, {
method:'GET',
headers:headers
})
.then((response) => response.json())
.then((response) => {
resolve(response);
})
.catch((error) => {
reject({status:-1})
})
.done();
})
}
  • 好,这边我们 GET 就封装好了,简单使用一下:
	fetchData(resolve) {
HTTPBase.get('http://guangdiu.com/api/gethots.php')
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.data),
loaded:true,
});
if (resolve !== undefined){
setTimeout(() => {
resolve(); // 关闭动画
}, 1000);
}
})
.catch((error) => { })
} export default HTTPBase;
  • 接着,我们继续来对 POST 进行封装:
	/**
*
* POST请求
*
* @param url
* @param params {}包装
* @param headers
*
* @return {Promise}
*
* */
HTTPBase.post = function (url, params, headers) {
if (params) {
// 初始化FormData
var formData = new FormData(); // 获取 params 内所有的 key
let paramsKeyArray = Object.keys(params);
// 通过 forEach 方法拿到数组中每个元素,将元素与参数的值进行拼接处理,并且放入 paramsArray 中
paramsKeyArray.forEach(key => formData.append(key, params[key]));
} return new Promise(function (resolve, reject) {
fetch(url, {
method:'POST',
headers:headers,
body:formData,
})
.then((response) => response.json())
.then((response) => {
resolve(response);
})
.catch((error) => {
reject({status:-1})
})
.done();
})
} export default HTTPBase;
  • 好,来试一下:
	// 网络请求
fetchData(resolve) { let params = {"count" : 5 }; HTTPBase.post('http://guangdiu.com/api/getlist.php', params)
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.data),
loaded:true,
});
if (resolve !== undefined){
setTimeout(() => {
resolve();
}, 1000);
}
})
.catch((error) => { })
}
  • 这次篇幅有点短,实在是太忙了!

React-Native 之 项目实战(二)的更多相关文章

  1. React Native 之 项目实战(一)

    前言 本文有配套视频,可以酌情观看. 文中内容因各人理解不同,可能会有所偏差,欢迎朋友们联系我. 文中所有内容仅供学习交流之用,不可用于商业用途,如因此引起的相关法律法规责任,与我无关. 如文中内容对 ...

  2. React Native商城项目实战04 - 封装TabNavigator.Item的创建

    1.Main.js /** * 主页面 */ import React, { Component } from 'react'; import { StyleSheet, Text, View, Im ...

  3. React Native商城项目实战02 - 主要框架部分(tabBar)

    1.安装插件,cd到项目根目录下执行: $ npm i react-native-tab-navigator --save 2.主框架文件Main.js /** * 主页面 */ import Rea ...

  4. React Native商城项目实战01 - 初始化设置

    1.创建项目 $ react-native init BuyDemo 2.导入图片资源 安卓:把文件夹放到/android/app/src/main/res/目录下,如图: iOS: Xcode打开工 ...

  5. React Native商城项目实战07 - 设置“More”界面导航条

    1.More/More.js /** * 更多 */ import React, { Component } from 'react'; import { AppRegistry, StyleShee ...

  6. React Native商城项目实战05 - 设置首页的导航条

    1.Home.js /** * 首页 */ import React, { Component } from 'react'; import { AppRegistry, StyleSheet, Te ...

  7. React Native商城项目实战06 - 设置安卓中的启动页

    1.Main 目录下新建LaunchImage.js: /** * 启动页 */ import React, { Component } from 'react'; import { AppRegis ...

  8. React Native商城项目实战03 - 包装Navigator

    1.在Home目录下新建首页详细页HomeDetail.js /** * 首页详情页 */ import React, { Component } from 'react'; import { App ...

  9. React Native商城项目实战16 - 购物中心详细页

    逻辑分析: 首页(Home)加载的购物中心组件(ShopCenter),传递url数据: ShopCenter里根据url加载购物中心详细页组件(ShopCenterDetail), ShopCent ...

  10. React Native商城项目实战15 - 首页购物中心

    1.公共的标题栏组件TitleCommonCell.js /** * 首页购物中心 */ import React, { Component } from 'react'; import { AppR ...

随机推荐

  1. ArcGIS API for JavaScript 4.2学习笔记[5] 官方API大章节概述与内容转译

    内容如上,截图自ESRI官网,连接:ArcGIS API for JavaScript 4.2 [Get Started] 类似于绪论一样的东西,抽取了最需要关注的几个例子.如:加载Map和View, ...

  2. linux 标准目录

    转自 http://www.weixuehao.com/archives/492 装完Linux,首先需要弄清Linux 标准目录结构 / root -?启动Linux时使用的一些核心文件.如操作系统 ...

  3. 双显卡笔记本安装CUDA+theano、tensorflow环境

    原文出处:http://www.cnblogs.com/jacklu/p/6377820.html 个人知乎主页欢迎关注:https://www.zhihu.com/people/jack_lu,相信 ...

  4. 《微信小程序七日谈》- 第七天:不要捡了芝麻丢了西瓜

    <微信小程序七日谈>系列文章: 第一天:人生若只如初见: 第二天:你可能要抛弃原来的响应式开发思维: 第三天:玩转Page组件的生命周期: 第四天:页面路径最多五层?导航可以这么玩: 第五 ...

  5. OSS.Common扩展.Net Standard支持实例分享

    上篇(.Net基础体系和跨框架开发普及)介绍了.Net当前生态下的大概情况,也分享了简单实现的过程,这篇文章就是讲解我的OSS.Common项目扩展.Net Standard 支持的过程,主要集中在: ...

  6. 一篇文章搞定css3 3d效果

    css3 3d学习心得 卡片反转 魔方 banner图 首先我们要学习好css3 3d一定要有一定的立体感 通过这个图片应该清楚的了解到了x轴 y轴 z轴是什么概念了. 首先先给大家看一个小例子: 卡 ...

  7. gevent拾遗

      在前文已经介绍过了gevent的调度流程,本文介绍gevent一些重要的模块,包括Timeout,Event\AsynResult, Semphore, socket patch,这些模块都涉及当 ...

  8. javascript学习-对象与原型

    javascript学习-对象与原型 Javascript语言是符合面向对象思想的.一般来说,面向对象思想需要满足以下三个基本要求: 封装,Javascript的对象可以自由的扩充成员变量和方法,自然 ...

  9. React 国际化

    前言 React 做国际化,我推荐使用 React-intl , 这个库提供了 React 组件和Api两种方式来格式化日期,数字和字符串等.知道这个库了,那让我们开始使用它 组件用法 为了和Reac ...

  10. Vim进阶命令

    1. 查找    /xxx(?xxx)       表示在整篇文档中搜索匹配xxx的字符串, / 表示向下查找, ? 表示                    向上查找.其中xxx可以是正规表达式, ...