快速学习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="红色"/>
    );
  }
}
复制代码

看到了吧, 这一句引用了子组件,并指定了该子组件的属性值,值就是在这里设定的,传递到子组件后就是固定了不能改变了.

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

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 改变苹果组件的样式(颜色和数量变化),先看下效果

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

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的使用 通过网络请求一个文章列表数据 进行展示 先看效果

首先,新建一个组件,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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值