學(xué)習(xí)目標(biāo)
繼上一篇學(xué)習(xí)了RN的環(huán)境搭建和demo的運(yùn)行應(yīng)該有了一定的了解,接下來(lái)我們就學(xué)習(xí)影響頁(yè)面變化的兩個(gè)重要元素props和state
代碼調(diào)試
在介紹這倆兄弟前,先補(bǔ)充一下demo的運(yùn)行及調(diào)試方法 ,后面斷點(diǎn)調(diào)試及瀏覽器調(diào)試再詳細(xì)介紹
在react-native run-android
執(zhí)行成功跑起來(lái)后,如果想修改頁(yè)面和修改代碼邏輯后看效果,不需要像原生一樣,再run一次,安卓的項(xiàng)目如果moudle比較多的話(huà)building的時(shí)間真的是很長(zhǎng).
搖一搖會(huì)彈出一個(gè)菜單,開(kāi)啟Enable Live Reload
VSCode左下角設(shè)置,進(jìn)入命令面板搜索 React Native: Start Packager 找到后點(diǎn)擊啟動(dòng)
待啟動(dòng)成功后,可以嘗試修改代碼然后手動(dòng)保存 ctrl+s 就會(huì)自動(dòng)更新代碼看到效果了
調(diào)試說(shuō)完了,下面開(kāi)始介紹倆兄弟
props :
我個(gè)人理解,該屬性的值是在父組件中引用子組件時(shí)候指定的,該屬性是子組件中定義的,比如新建一個(gè)蘋(píng)果組件,蘋(píng)果有顏色的屬性,在構(gòu)建這個(gè)組件的時(shí)候,指定一個(gè)color屬性,在父組件使用的時(shí)候再指定一個(gè)具體的顏色值,那么最后子組件就會(huì)拿到這個(gè)值,顯示相應(yīng)顏色的蘋(píng)果.
舉例說(shuō)明:
新建一個(gè)蘋(píng)果組件 AppleComponent
import * as React from "react";
import { Text, View } from "react-native";
/**
* 定義屬性接口
*/
interface AppleComponentProps{
//蘋(píng)果組件顏色屬性
apple_color:string
}
/**
* 自定義一個(gè)蘋(píng)果組件
*/
export default class AppleComponent extends React.Component<AppleComponentProps>{
constructor(props:any){
super(props);
}
render(){
//什么顏色,由父親(父組件)來(lái)定 當(dāng)然也可以自己給自己定義一個(gè)默認(rèn)的樣子,下面會(huì)說(shuō)
return(
<View>
<Text>我是一個(gè){this.props.apple_color}的蘋(píng)果組件</Text>
</View>
)
}
}
我們?cè)谔O(píng)果組件里定義了一個(gè)屬性apple_color 用于設(shè)定他的顏色
在render時(shí)候通過(guò)this.props.apple_color來(lái)獲取使用 它的具體值來(lái)自父組件的指定 看父組件就明白了
//導(dǎo)入蘋(píng)果組件的路徑 根據(jù)你自己的路徑
import AppleComponent from './src/Apple'
//該組件是啟動(dòng)的首頁(yè)組件
export default class App extends Component<Props> {
render() {
return (
<AppleComponent apple_color="紅色"/>
);
}
}
看到了吧,<AppleComponent apple_color="紅色"/> 這一句引用了子組件,并指定了該子組件的屬性值,值就是在這里設(shè)定的,傳遞到子組件后就是固定了不能改變了.
這里只是傳遞了一個(gè)屬性,實(shí)際情況會(huì)有很多,這樣一個(gè)個(gè)傳比較麻煩 下面貼出多屬性時(shí)候的寫(xiě)法
interface AppleComponentProps{
//蘋(píng)果組件顏色屬性
apple_color:string
//蘋(píng)果組件重量屬性
apple_weight:number
//蘋(píng)果組件價(jià)格屬性
apple_price:number
}
/**
* 自定義一個(gè)蘋(píng)果組件
*/
export default class AppleComponent extends React.Component<AppleComponentProps>{
constructor(props:any){
super(props);
}
render(){
//什么顏色,由父親(父組件)來(lái)定 當(dāng)然也可以自己給自己定義一個(gè)默認(rèn)的樣子
return(
<View>
<Text>我是一個(gè){this.props.apple_color}的蘋(píng)果組件</Text>
<Text>我的重量是:{this.props.apple_weight}kg</Text>
<Text>我的價(jià)格是:{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}/>
);
}
}
在一個(gè)參數(shù)的基礎(chǔ)上依次增加了兩個(gè)屬性,使用方法和上面一致,可以看到,傳遞的屬性值在比較多的情況下寫(xiě)法還是比較麻煩的,下面貼出比較簡(jiǎn)單的寫(xiě)法
export default class App extends Component<Props> {
render() {
var params = { apple_color :'紅色', apple_weight: 1.8, apple_price: 9.9}
return (
<AppleComponent {...params}/>
);
}
}
{...params} 的寫(xiě)法意思代表了把全部的參數(shù)一起傳遞過(guò)去了, 這樣簡(jiǎn)單多了吧.
當(dāng)然如果有時(shí)候想只傳遞部分屬性 也是可以的.使用解構(gòu)賦值
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}/>
);
}
}
但是這樣寫(xiě) 編譯器會(huì)提示有錯(cuò)誤 少了一個(gè)屬性,因?yàn)檫@里我使用了接口的方式定義了屬性 通過(guò)泛型傳遞給了
interface AppleComponentProps{
//蘋(píng)果組件顏色屬性
apple_color:string
//蘋(píng)果組件重量屬性
apple_weight:number
//蘋(píng)果組件價(jià)格屬性
apple_price:number
}
export default class AppleComponent extends React.Component<AppleComponentProps>{...}
因此,有了接口的限定,只能按照接口的規(guī)范不能缺少屬性,全部要覆寫(xiě).
這時(shí)候如果比較倔強(qiáng),就不想全部屬性賦值傳遞呢? 好的,經(jīng)過(guò)探索,可以的,了解kotlin的應(yīng)該知道,有一個(gè)可空的特性,使用?標(biāo)識(shí),對(duì),一樣的,我們只需要在屬性上加一個(gè)可空標(biāo)識(shí),那么這個(gè)屬性就可以不傳了.
apple_price?:number
這樣就不會(huì)提示錯(cuò)誤了
還有一種方式那就是不用接口的方式定義屬性,直接在泛型里傳入any任意值 也是不會(huì)提示錯(cuò)的,想怎么傳自己隨便了.
React.Component<any>
接下來(lái)你會(huì)發(fā)現(xiàn),如果部分屬性值沒(méi)有指定,那么ui上本來(lái)要顯示的內(nèi)容,由于部分參數(shù)沒(méi)傳,就會(huì)顯示空了,那么就有一個(gè)給屬性設(shè)定默認(rèn)值的方法防止值為空 這里只是為了學(xué)習(xí) 實(shí)際上并不一定會(huì)用的上
//給屬性設(shè)定默認(rèn)的值
static defaultProps = {
apple_color: "綠色",
apple_weight:2.5,
apple_price:19.9
};
//也可以這樣寫(xiě) 推薦直接這樣寫(xiě) 上面的是js的時(shí)候的寫(xiě)法
//給屬性設(shè)定默認(rèn)的值
private apple_color: string = "綠色";
private apple_weight: number = 2.5;
private apple_price: number = 19.9;
//注意,在構(gòu)造函數(shù)中進(jìn)行賦值
this.apple_color = this.props.apple_color;
this.apple_weight = this.props.apple_weight;
//由于該屬性設(shè)置了可空 ts檢測(cè)不能直接賦值,采用如下寫(xiě)法即可
this.apple_price = this.props.apple_price? this.props.apple_price:this.apple_price
在組件的最開(kāi)始地方加上默認(rèn)值 若父組件沒(méi)有賦值,那么就顯示默認(rèn)的值 這里只有價(jià)格沒(méi)傳,使用的是默認(rèn)的價(jià)格
以上就是我對(duì)props的理解,對(duì)于屬性類(lèi)型還有一個(gè)約束限定類(lèi)型的內(nèi)容,這塊感覺(jué)暫時(shí)簡(jiǎn)單說(shuō)下,后面遇到有用的地方再詳細(xì)說(shuō)明,其實(shí)就是對(duì)屬性的類(lèi)型就行類(lèi)型的指定,如果在指定屬性值時(shí)候類(lèi)型不匹配,編譯器便會(huì)提示錯(cuò)誤.
首先添加依賴(lài)庫(kù)
yarn add prop-types
組件中導(dǎo)入
import PropTypes from "prop-types";
組件中使用 舉例
static propTypes = {
name: PropTypes.string.isRequired, //isRequired就是不可空 必傳屬性
age: PropTypes.number, //指定數(shù)字類(lèi)型
sex: PropTypes.string //指定字符串類(lèi)型
};
關(guān)于props還有很多需要深入理解的,先暫時(shí)研究到這,我們需要快速掌握使用.
state:
我理解是在組件本身內(nèi)部屬性變量,props是外部指定賦值,一旦賦值,在組件內(nèi)接收后便不會(huì)再改邊,這樣對(duì)于UI的刷新,動(dòng)態(tài)變化是做不到支持的,所以就有了state這個(gè)對(duì)象來(lái)控制頁(yè)面狀態(tài)的刷新變化
舉例說(shuō)明
還拿上面的蘋(píng)果組件來(lái)說(shuō),顏色,價(jià)格,重量都是指定該組件的屬性,在初始化前就定義好了樣式的,如果想在這之后改變蘋(píng)果組件的狀態(tài),比如點(diǎn)擊蘋(píng)果組件 改變顏色或者改變他的顯示內(nèi)容等等
實(shí)際上應(yīng)該用在加載網(wǎng)絡(luò)數(shù)據(jù)多一些吧
這里實(shí)現(xiàn)每過(guò)1s 改變蘋(píng)果組件的樣式(顏色和數(shù)量變化),先看下效果
首先通過(guò)接口定義我們需要的狀態(tài) 參考代碼:
interface AppleComponentState {
//是否改變顏色
ischange: boolean;
//蘋(píng)果數(shù)量
apple_num: number;
}
//傳入Component<P,S> 對(duì)應(yīng)的S的泛型中 前面P對(duì)應(yīng)的props屬性接口對(duì)象
export default class AppleComponent extends React.Component<AppleComponentProps,AppleComponentState> {....}
在構(gòu)造函數(shù)中進(jìn)行狀態(tài)變量的賦值
//構(gòu)造函數(shù)
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
//狀態(tài)賦值
this.state = {
ischange: false,
apple_num : 1
};
//執(zhí)行狀態(tài)變化的函數(shù)
setInterval(() => {
this.setState(previousState => {
return {
ischange: !previousState.ischange,
apple_num: previousState.apple_num + 1
};
});
}, 1000);
}
上面代碼中setInterval()函數(shù)即進(jìn)行了蘋(píng)果組件狀態(tài)變化的操作,動(dòng)態(tài)改變蘋(píng)果組件ischange,apple_num的值,來(lái)刷新界面顯示的內(nèi)容 最終調(diào)用的是this.setState()函數(shù)進(jìn)行狀態(tài)的改變, previousState 為上一次狀態(tài)對(duì)象,我理解就是每次setState都會(huì)保存一個(gè)state對(duì)象存入一個(gè)對(duì)象的隊(duì)列中,類(lèi)似棧,這塊理解不深,暫且不說(shuō)了,通過(guò)previousState 引用可以拿到的是上一次的狀態(tài)對(duì)象即可.
下面是渲染的參考代碼 也就是狀態(tài)變量的調(diào)用 this.state.xxxxx 獲取的是狀態(tài)更新后的最新的值
render() {
//什么顏色,由父組件來(lái)定 當(dāng)然也可以自己給自己定義一個(gè)默認(rèn)的樣子
let colorvalue = this.state.ischange ? "#ff0033" : "#000000";
return (
<View style={styles.container}>
<Text style={{ color: colorvalue }}>
我是一個(gè)
{this.apple_color}
的蘋(píng)果組件
</Text>
<Text style={{ color: colorvalue }}>
我的重量是:
{this.apple_weight}
kg
</Text>
<Text style={{ color: colorvalue }}>
我的價(jià)格是:
{this.apple_price}元
</Text>
<Text style={{ fontSize: 20, margin: 10, color: colorvalue }}>
{this.state.apple_num}
</Text>
</View>
);
}
以上是通過(guò)一個(gè)舉例來(lái)理解state的一個(gè)使用,實(shí)際應(yīng)用開(kāi)發(fā)中一般很少用到了,下面就拿一個(gè)實(shí)際應(yīng)用開(kāi)發(fā)的簡(jiǎn)單的業(yè)務(wù)來(lái)實(shí)踐一下state的使用 通過(guò)網(wǎng)絡(luò)請(qǐng)求一個(gè)文章列表數(shù)據(jù) 進(jìn)行展示 先看效果
首先,新建一個(gè)組件,React是一個(gè)組件化思想的框架,所以寫(xiě)一個(gè)頁(yè)面基本都是新建一個(gè)Component,這里命名為 HomeArtical.tsx
第一步: 定義屬性和狀態(tài) 先別急 分析一下 雖然功能簡(jiǎn)單,通過(guò)網(wǎng)絡(luò)請(qǐng)求 獲取數(shù)據(jù) 列表展示
這里面可能需要用到的屬性有哪些呢?
屬性: 那些用于函數(shù)業(yè)務(wù)邏輯控制的一些變量
- url:string //接口請(qǐng)求地址
- curPageIndex;number //當(dāng)前頁(yè)碼索引
- pageSizes:number //每頁(yè)數(shù)據(jù)大小
- .....
做分頁(yè)加載 下拉刷新 上拉加載更多 等等會(huì)用很多 根據(jù)需要自行增加
狀態(tài): 那些用于界面刷新相關(guān)的變量
- data:[] //數(shù)據(jù)源肯定要有 也是唯一必須的
- ....
下面是為實(shí)現(xiàn)列表數(shù)據(jù)單純的展示 定義的屬性和狀態(tài) 參考代碼:
//屬性
interface HomeArticalProps {
//接口地址
artical_url?: string;
//當(dāng)前頁(yè)碼索引
page_index?: number;
}
//狀態(tài)
interface HomeArticalStates {
//文章數(shù)據(jù)源
data: string[];
}
//傳入泛型中
export default class HomeArtical extends React.Component<
HomeArticalProps,
HomeArticalStates
> {......}
這里在說(shuō)明一下 為啥用接口來(lái)定義屬性狀態(tài)呢,我想是為了外部父組件傳入屬性的時(shí)候使用this.props.xxxx即可獲取到傳入的屬性值.在vscode編輯器也會(huì)有屬性提示和錯(cuò)誤檢查,由于剛學(xué)習(xí),也是參考了同事的代碼來(lái)寫(xiě)的,寫(xiě)到這,我要去看typescript文檔了,不能誤人子弟啊 不會(huì)TypeScript的去多看看文檔 TypeScript.聲明合并 的接口合并 TypeScript 文檔
下面繼續(xù),定義好了屬性和狀態(tài)后開(kāi)始賦值
默認(rèn)賦值了一些初始值
//頁(yè)碼
private page_index: number = 0;
//請(qǐng)求接口
private artical_url: string = "http://www.wanandroid.com/article/list/";
//請(qǐng)求url, 默認(rèn)加載首頁(yè)文章列表第一頁(yè)
private artical_api: string = this.artical_url + this.page_index + "/json";
構(gòu)造函數(shù)給屬性和狀態(tài)賦值 值來(lái)自父組件或本身默認(rèn)值
constructor(props: HomeArticalProps) {
super(props);
//子組件內(nèi)部屬性引用重新賦值 值來(lái)自父組件
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 = {
//數(shù)據(jù)源
data: []
};
編寫(xiě)界面 這里只需要用到一個(gè)列表組件 使用原生的FlatList即可 參考代碼:
//渲染頁(yè)面
public render() {
return (
<FlatList //列表組件
style={styles.container} //樣式
data={this.state.data} //綁定數(shù)據(jù)源
keyExtractor={this._keyExtractor} //每條數(shù)據(jù)設(shè)置不一樣的key
ListHeaderComponent={this.renderHeader} //列表頭部視圖
ItemSeparatorComponent={this.renderSeparator} //分割線(xiàn)
renderItem={this.renderItem} //item條目
/>
);
}
編寫(xiě)網(wǎng)絡(luò)請(qǐng)求 使用原生的fetch
//獲取數(shù)據(jù)
private getArticalDatas(curPage: number) {
fetch(this.artical_api)
.then(response => response.json())
.then(responseData => {
this.setState({
//修改數(shù)據(jù)源 刷新界面
data: this.state.data.concat(responseData.data.datas)
});
})
.catch(error => {
this.setState({});
});
}
開(kāi)始請(qǐng)求數(shù)據(jù)
組件的生命周期的界面渲染完成后開(kāi)始加載數(shù)據(jù)
//開(kāi)始請(qǐng)求數(shù)據(jù)
componentDidMount() {
this.getArticalDatas(0);
}
至此這個(gè)頁(yè)面就完成了,當(dāng)然加上loading 和 上拉加載更多 下拉刷新會(huì)更完整點(diǎn) 之前寫(xiě)過(guò)js版本 不過(guò)要換成ts的寫(xiě)法 我還沒(méi)有改,后面慢慢加入.這次先學(xué)會(huì)屬性和狀態(tài)的使用和個(gè)人的理解.
寫(xiě)了這么多 其實(shí)都是最基礎(chǔ)的用法了,深入理解的東西還要再實(shí)際項(xiàng)目中慢慢悟.本篇是快速學(xué)習(xí),直接上手項(xiàng)目開(kāi)發(fā)的筆記,用于加深記憶,如果有一起學(xué)習(xí)的可以和我一起.持續(xù)更新.
計(jì)劃后面學(xué)習(xí)
- 頁(yè)面跳轉(zhuǎn)及參數(shù)傳遞第三方庫(kù)的使用
- tsx中引用js的聲明
- mobx狀態(tài)管理
- 代碼調(diào)試
- 打包發(fā)布
- 熱更新
- 性能優(yōu)化及深入理解
- ......
查閱完整demo中的代碼 可以去我的github 地址是:RNDemo