快速学习RN之属性props和状态state(二)

学习目标

继上一篇学习了RN的环境搭建和demo的运行应该有了一定的了解,接下来我们就学习影响页面变化的两个重要元素propsstate

代码调试

在介绍这俩兄弟前,先补充一下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<AppleComponentProps>{
    constructor(props:any){
        super(props);
    }
    render(){
        //什么颜色,由父亲(父组件)来定 当然也可以自己给自己定义一个默认的样子,下面会说
        return(
            <View>
                <Text>我是一个{this.props.apple_color}的苹果组件</Text>
            </View>
        )
    }

}

我们在苹果组件里定义了一个属性apple_color 用于设定他的颜色

在render时候通过this.props.apple_color来获取使用 它的具体值来自父组件的指定 看父组件就明白了

//导入苹果组件的路径 根据你自己的路径
import AppleComponent from './src/Apple'
//该组件是启动的首页组件 
export default class App extends Component<Props> {
  render() {
    return (
      <AppleComponent apple_color="红色"/>
    );
  }
}

看到了吧,<AppleComponent apple_color="红色"/> 这一句引用了子组件,并指定了该子组件的属性值,值就是在这里设定的,传递到子组件后就是固定了不能改变了.

这里只是传递了一个属性,实际情况会有很多,这样一个个传比较麻烦 下面贴出多属性时候的写法

interface AppleComponentProps{
    //苹果组件颜色属性
    apple_color:string
    //苹果组件重量属性
    apple_weight:number
    //苹果组件价格属性
    apple_price:number
}
/**
 * 自定义一个苹果组件
 */
export default class AppleComponent extends React.Component<AppleComponentProps>{
    constructor(props:any){
        super(props);
    }
    render(){
        //什么颜色,由父亲(父组件)来定 当然也可以自己给自己定义一个默认的样子
        return(
            <View>
                <Text>我是一个{this.props.apple_color}的苹果组件</Text>
                <Text>我的重量是:{this.props.apple_weight}kg</Text>
                <Text>我的价格是:{this.props.apple_price}元</Text>
            </View>
        )
    }

}
export default class App extends Component<Props> {
  render() {
    return (
      <AppleComponent apple_color="红色" apple_weight={1.8} apple_price={9.9}/>
    );
  }
}

在一个参数的基础上依次增加了两个属性,使用方法和上面一致,可以看到,传递的属性值在比较多的情况下写法还是比较麻烦的,下面贴出比较简单的写法

export default class App extends Component<Props> {
  render() {
    var params = { apple_color :'红色', apple_weight: 1.8, apple_price: 9.9}
    return (
      <AppleComponent {...params}/>
    );
  }
}

{...params} 的写法意思代表了把全部的参数一起传递过去了, 这样简单多了吧.

当然如果有时候想只传递部分属性 也是可以的.使用解构赋值

export default class App extends Component<Props> {
  render() {
    var params = { apple_color :'红色', apple_weight: 1.8, apple_price: 9.9}
    var { apple_color,apple_weight} = params
    return (
      <AppleComponent apple_color={apple_color},apple_weight={apple_weight}/>
    );
  }
}

但是这样写 编译器会提示有错误 少了一个属性,因为这里我使用了接口的方式定义了属性 通过泛型传递给了

interface AppleComponentProps{
    //苹果组件颜色属性
    apple_color:string
    //苹果组件重量属性
    apple_weight:number
    //苹果组件价格属性
    apple_price:number
}
export default class AppleComponent extends React.Component<AppleComponentProps>{...}

因此,有了接口的限定,只能按照接口的规范不能缺少属性,全部要覆写.

这时候如果比较倔强,就不想全部属性赋值传递呢? 好的,经过探索,可以的,了解kotlin的应该知道,有一个可空的特性,使用?标识,对,一样的,我们只需要在属性上加一个可空标识,那么这个属性就可以不传了.

apple_price?:number

这样就不会提示错误了

还有一种方式那就是不用接口的方式定义属性,直接在泛型里传入any任意值 也是不会提示错的,想怎么传自己随便了.

React.Component<any>

接下来你会发现,如果部分属性值没有指定,那么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 改变苹果组件的样式(颜色和数量变化),先看下效果

压缩成gif频率的快了=.=!~

首先通过接口定义我们需要的状态 参考代码:

interface AppleComponentState {
  //是否改变颜色
  ischange: boolean;
   //苹果数量
  apple_num: number;
}
//传入Component<P,S> 对应的S的泛型中 前面P对应的props属性接口对象
export default class AppleComponent extends React.Component<AppleComponentProps,AppleComponentState> {....}

在构造函数中进行状态变量的赋值

 //构造函数
  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 (
      <View style={styles.container}>
        <Text style={{ color: colorvalue }}>
          我是一个
          {this.apple_color}
          的苹果组件
        </Text>
        <Text style={{ color: colorvalue }}>
          我的重量是:
          {this.apple_weight}
          kg
        </Text>
        <Text style={{ color: colorvalue }}>
          我的价格是:
          {this.apple_price}元
        </Text>
        <Text style={{ fontSize: 20, margin: 10, color: colorvalue }}>
          {this.state.apple_num}
        </Text>
      </View>
    );
  }

以上是通过一个举例来理解state的一个使用,实际应用开发中一般很少用到了,下面就拿一个实际应用开发的简单的业务来实践一下state的使用 通过网络请求一个文章列表数据 进行展示 先看效果


net_list_demo.png

首先,新建一个组件,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 (
      <FlatList //列表组件
        style={styles.container} //样式
        data={this.state.data} //绑定数据源
        keyExtractor={this._keyExtractor} //每条数据设置不一样的key
        ListHeaderComponent={this.renderHeader} //列表头部视图
        ItemSeparatorComponent={this.renderSeparator} //分割线
        renderItem={this.renderItem} //item条目
      />
    );
  }

编写网络请求 使用原生的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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • 作为一个合格的开发者,不要只满足于编写了可以运行的代码。而要了解代码背后的工作原理;不要只满足于自己的程序...
    六个周阅读 8,441评论 1 33
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明先生_X自主阅读 15,979评论 3 119
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • 简介 大多数组件在创建时就可以使用各种参数来进行定制。用于定制的这些参数就称为props(属性)。我们使用两种数据...
    邪人君子阅读 4,314评论 0 6
  • 品流才子作将军, 香浮宝辇仙风润。 茗饮蔗浆携所有, 醉下烟汀减去愁。 西岭松声落日秋, 湖色寒分半槛流。
    聊减肥话瘦身阅读 272评论 0 0