学习目标
继上一篇学习了RN的环境搭建和demo的运行应该有了一定的了解,接下来我们就学习影响页面变化的两个重要元素props和state
代码调试
在介绍这俩兄弟前,先补充一下demo的运行及调试方法 ,后面断点调试及浏览器调试再详细介绍
在react-native run-android
执行成功跑起来后,如果想修改页面和修改代码逻辑后看效果,不需要像原生一样,再run一次,安卓的项目如果moudle比较多的话building的时间真的是很长.
摇一摇会弹出一个菜单,开启Enable Live Reload
VSCode左下角设置,进入命令面板搜索 React Native: Start Packager 找到后点击启动
待启动成功后,可以尝试修改代码然后手动保存 ctrl+s 就会自动更新代码看到效果了
调试说完了,下面开始介绍俩兄弟
props :
我个人理解,该属性的值是在父组件中引用子组件时候指定的,该属性是子组件中定义的,比如新建一个苹果组件,苹果有颜色的属性,在构建这个组件的时候,指定一个color属性,在父组件使用的时候再指定一个具体的颜色值,那么最后子组件就会拿到这个值,显示相应颜色的苹果.
举例说明:
新建一个苹果组件 AppleComponent
import * as React from "react";
import { Text, View } from "react-native";
/**
* 定义属性接口
*/
interface AppleComponentProps{
//苹果组件颜色属性
apple_color:string
}
/**
* 自定义一个苹果组件
*/
export default class AppleComponent extends React.Component{
constructor(props:any){
super(props);
}
render(){
//什么颜色,由父亲(父组件)来定 当然也可以自己给自己定义一个默认的样子,下面会说
return(
我是一个{this.props.apple_color}的苹果组件
)
}
}
我们在苹果组件里定义了一个属性apple_color 用于设定他的颜色
在render时候通过this.props.apple_color来获取使用 它的具体值来自父组件的指定 看父组件就明白了
//导入苹果组件的路径 根据你自己的路径
import AppleComponent from './src/Apple'
//该组件是启动的首页组件
export default class App extends Component {
render() {
return (
);
}
}
看到了吧,
这里只是传递了一个属性,实际情况会有很多,这样一个个传比较麻烦 下面贴出多属性时候的写法
interface AppleComponentProps{
//苹果组件颜色属性
apple_color:string
//苹果组件重量属性
apple_weight:number
//苹果组件价格属性
apple_price:number
}
/**
* 自定义一个苹果组件
*/
export default class AppleComponent extends React.Component{
constructor(props:any){
super(props);
}
render(){
//什么颜色,由父亲(父组件)来定 当然也可以自己给自己定义一个默认的样子
return(
我是一个{this.props.apple_color}的苹果组件
我的重量是:{this.props.apple_weight}kg
我的价格是:{this.props.apple_price}元
)
}
}
export default class App extends Component {
render() {
return (
);
}
}
在一个参数的基础上依次增加了两个属性,使用方法和上面一致,可以看到,传递的属性值在比较多的情况下写法还是比较麻烦的,下面贴出比较简单的写法
export default class App extends Component {
render() {
var params = { apple_color :'红色', apple_weight: 1.8, apple_price: 9.9}
return (
);
}
}
{...params} 的写法意思代表了把全部的参数一起传递过去了, 这样简单多了吧.
当然如果有时候想只传递部分属性 也是可以的.使用解构赋值
export default class App extends Component {
render() {
var params = { apple_color :'红色', apple_weight: 1.8, apple_price: 9.9}
var { apple_color,apple_weight} = params
return (
);
}
}
但是这样写 编译器会提示有错误 少了一个属性,因为这里我使用了接口的方式定义了属性 通过泛型传递给了
interface AppleComponentProps{
//苹果组件颜色属性
apple_color:string
//苹果组件重量属性
apple_weight:number
//苹果组件价格属性
apple_price:number
}
export default class AppleComponent extends React.Component{...}
因此,有了接口的限定,只能按照接口的规范不能缺少属性,全部要覆写.
这时候如果比较倔强,就不想全部属性赋值传递呢? 好的,经过探索,可以的,了解kotlin的应该知道,有一个可空的特性,使用?标识,对,一样的,我们只需要在属性上加一个可空标识,那么这个属性就可以不传了.
apple_price?:number
这样就不会提示错误了
还有一种方式那就是不用接口的方式定义属性,直接在泛型里传入any任意值 也是不会提示错的,想怎么传自己随便了.
React.Component
接下来你会发现,如果部分属性值没有指定,那么ui上本来要显示的内容,由于部分参数没传,就会显示空了,那么就有一个给属性设定默认值的方法防止值为空 这里只是为了学习 实际上并不一定会用的上
//给属性设定默认的值
static defaultProps = {
apple_color: "绿色",
apple_weight:2.5,
apple_price:19.9
};
//也可以这样写 推荐直接这样写 上面的是js的时候的写法
//给属性设定默认的值
private apple_color: string = "绿色";
private apple_weight: number = 2.5;
private apple_price: number = 19.9;
//注意,在构造函数中进行赋值
this.apple_color = this.props.apple_color;
this.apple_weight = this.props.apple_weight;
//由于该属性设置了可空 ts检测不能直接赋值,采用如下写法即可
this.apple_price = this.props.apple_price? this.props.apple_price:this.apple_price
在组件的最开始地方加上默认值 若父组件没有赋值,那么就显示默认的值 这里只有价格没传,使用的是默认的价格
以上就是我对props的理解,对于属性类型还有一个约束限定类型的内容,这块感觉暂时简单说下,后面遇到有用的地方再详细说明,其实就是对属性的类型就行类型的指定,如果在指定属性值时候类型不匹配,编译器便会提示错误.
首先添加依赖库
yarn add prop-types
组件中导入
import PropTypes from "prop-types";
组件中使用 举例
static propTypes = {
name: PropTypes.string.isRequired, //isRequired就是不可空 必传属性
age: PropTypes.number, //指定数字类型
sex: PropTypes.string //指定字符串类型
};
关于props还有很多需要深入理解的,先暂时研究到这,我们需要快速掌握使用.
state:
我理解是在组件本身内部属性变量,props是外部指定赋值,一旦赋值,在组件内接收后便不会再改边,这样对于UI的刷新,动态变化是做不到支持的,所以就有了state这个对象来控制页面状态的刷新变化
举例说明
还拿上面的苹果组件来说,颜色,价格,重量都是指定该组件的属性,在初始化前就定义好了样式的,如果想在这之后改变苹果组件的状态,比如点击苹果组件 改变颜色或者改变他的显示内容等等
实际上应该用在加载网络数据多一些吧
这里实现每过1s 改变苹果组件的样式(颜色和数量变化),先看下效果
首先通过接口定义我们需要的状态 参考代码:
interface AppleComponentState {
//是否改变颜色
ischange: boolean;
//苹果数量
apple_num: number;
}
//传入Component 对应的S的泛型中 前面P对应的props属性接口对象
export default class AppleComponent extends React.Component {....}
在构造函数中进行状态变量的赋值
//构造函数
constructor(props: AppleComponentProps) {
super(props);
//属性赋值
this.apple_color = this.props.apple_color;
this.apple_weight = this.props.apple_weight;
this.apple_price = this.props.apple_price? this.props.apple_price:this.apple_price
//状态赋值
this.state = {
ischange: false,
apple_num : 1
};
//执行状态变化的函数
setInterval(() => {
this.setState(previousState => {
return {
ischange: !previousState.ischange,
apple_num: previousState.apple_num + 1
};
});
}, 1000);
}
上面代码中setInterval()函数即进行了苹果组件状态变化的操作,动态改变苹果组件ischange,apple_num的值,来刷新界面显示的内容 最终调用的是this.setState()函数进行状态的改变, previousState 为上一次状态对象,我理解就是每次setState都会保存一个state对象存入一个对象的队列中,类似栈,这块理解不深,暂且不说了,通过previousState 引用可以拿到的是上一次的状态对象即可.
下面是渲染的参考代码 也就是状态变量的调用 this.state.xxxxx 获取的是状态更新后的最新的值
render() {
//什么颜色,由父组件来定 当然也可以自己给自己定义一个默认的样子
let colorvalue = this.state.ischange ? "#ff0033" : "#000000";
return (
我是一个
{this.apple_color}
的苹果组件
我的重量是:
{this.apple_weight}
kg
我的价格是:
{this.apple_price}元
{this.state.apple_num}
);
}
以上是通过一个举例来理解state的一个使用,实际应用开发中一般很少用到了,下面就拿一个实际应用开发的简单的业务来实践一下state的使用 通过网络请求一个文章列表数据 进行展示 先看效果
首先,新建一个组件,React是一个组件化思想的框架,所以写一个页面基本都是新建一个Component,这里命名为 HomeArtical.tsx
第一步: 定义属性和状态 先别急 分析一下 虽然功能简单,通过网络请求 获取数据 列表展示
这里面可能需要用到的属性有哪些呢?
属性: 那些用于函数业务逻辑控制的一些变量
- url:string //接口请求地址
- curPageIndex;number //当前页码索引
- pageSizes:number //每页数据大小
- .....
做分页加载 下拉刷新 上拉加载更多 等等会用很多 根据需要自行增加
状态: 那些用于界面刷新相关的变量
- data:[] //数据源肯定要有 也是唯一必须的
- ....
下面是为实现列表数据单纯的展示 定义的属性和状态 参考代码:
//属性
interface HomeArticalProps {
//接口地址
artical_url?: string;
//当前页码索引
page_index?: number;
}
//状态
interface HomeArticalStates {
//文章数据源
data: string[];
}
//传入泛型中
export default class HomeArtical extends React.Component<
HomeArticalProps,
HomeArticalStates
> {......}
这里在说明一下 为啥用接口来定义属性状态呢,我想是为了外部父组件传入属性的时候使用this.props.xxxx即可获取到传入的属性值.在vscode编辑器也会有属性提示和错误检查,由于刚学习,也是参考了同事的代码来写的,写到这,我要去看typescript文档了,不能误人子弟啊 不会TypeScript的去多看看文档 TypeScript.声明合并 的接口合并 TypeScript 文档
下面继续,定义好了属性和状态后开始赋值
默认赋值了一些初始值
//页码
private page_index: number = 0;
//请求接口
private artical_url: string = "http://www.wanandroid.com/article/list/";
//请求url, 默认加载首页文章列表第一页
private artical_api: string = this.artical_url + this.page_index + "/json";
构造函数给属性和状态赋值 值来自父组件或本身默认值
constructor(props: HomeArticalProps) {
super(props);
//子组件内部属性引用重新赋值 值来自父组件
this.artical_url = this.props.artical_url
? this.props.artical_url
: this.artical_url;
this.page_index = this.props.page_index
? this.props.page_index
: this.page_index;
this.artical_api = this.artical_url + this.page_index + "/json";
this.state = {
//数据源
data: []
};
编写界面 这里只需要用到一个列表组件 使用原生的FlatList即可 参考代码:
//渲染页面
public render() {
return (
);
}
编写网络请求 使用原生的fetch
//获取数据
private getArticalDatas(curPage: number) {
fetch(this.artical_api)
.then(response => response.json())
.then(responseData => {
this.setState({
//修改数据源 刷新界面
data: this.state.data.concat(responseData.data.datas)
});
})
.catch(error => {
this.setState({});
});
}
开始请求数据
组件的生命周期的界面渲染完成后开始加载数据
//开始请求数据
componentDidMount() {
this.getArticalDatas(0);
}
至此这个页面就完成了,当然加上loading 和 上拉加载更多 下拉刷新会更完整点 之前写过js版本 不过要换成ts的写法 我还没有改,后面慢慢加入.这次先学会属性和状态的使用和个人的理解.
写了这么多 其实都是最基础的用法了,深入理解的东西还要再实际项目中慢慢悟.本篇是快速学习,直接上手项目开发的笔记,用于加深记忆,如果有一起学习的可以和我一起.持续更新.
计划后面学习
- 页面跳转及参数传递第三方库的使用
- tsx中引用js的声明
- mobx状态管理
- 代码调试
- 打包发布
- 热更新
- 性能优化及深入理解
- ......
查阅完整demo中的代码 可以去我的github 地址是:RNDemo