1. 程式人生 > >React Native開發之快速入門React

React Native開發之快速入門React

前言

這篇文章,是接著之前的一篇入門文章寫的(雖然已經過去大半年了),本文的受眾仍然是React小白,熟悉React的同學可以不看了。上一篇文章連結:

上一篇文章主要介紹了JS的語言基礎和React的component生命週期。本文會接著上一篇文章,繼續講解React的基礎。

本文的React Native版本是0.0.40,開發IDE是Atom+Nuclide,IDE環境搭建可以參考我的這一篇文章。

如果你有足夠的時間,非常建議看看React的官方Getting start文件。

準備工作

初始化一個RN的工程,

react-native init ReactBasics

然後,在iOS模擬器(安卓也可以)上執行這個工程。

react-native run-ios

本文所有的程式碼修改都在index.ios.js中(如果是安卓模擬器,修改index.andorid.js).

預設的ReactBasics程式碼是這樣子的

//...
export default class ReactBasics extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Welcome to React Native!
        </Text>
        <Text style={styles.instructions}>
          To get started, edit index.android.js
        </Text>
        <Text style={styles.instructions}>
          Double tap R on your keyboard to reload,{'\n'
} Shake or press menu button for dev menu </Text> </View> ); } } //...

我們修改為這樣

export default class ReactBasics extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Let us learn React.
        </Text>
      </View>
    );
  }
}

然後,執行模擬器

react-native run-ios

看到的如下截圖:

React是啥?

React是一個Javascript框架,用來開發web應用。Web應用開發中,比較流行的有三個框架:

從名字上,就能看到React Native是基於React(都是Facebook出品)。React的設計思想是:

  • Declarative(互動式的)。

    應用都是基於狀態的,應用會隨著資料的變化切換到不同的狀態。React將這種狀態抽象為一個個View,這樣狀態的改變後,利用React就在不同的View之前切換。這樣,讓程式碼更清晰可預測,頁方便測試。

  • Component-Based(基於元件)

    把管理狀態的View封裝成Component,然後再把這些Component組合到一起來實現複雜的UI。

  • Learn Once, Write Anywhere(寫一次,到處跑)

    React支援Web開發,Server開發(Node),同樣也支援本文提到的App開發(React Native)。

JSX

JSX是JavaScript語言的擴充套件,它並不改變JS本身語法。使用起來類似XML,React會對JSX的程式碼進行編譯(編譯器是babel),生成JavaScript程式碼,用來描述React中的Element如何渲染。
本文最初的這段程式碼就是JSX語法。

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.welcome}>
          Let us learn React.
        </Text>
      </View>
    );
  }

其中

 <Text style={styles.welcome}>
    Let us learn React.
 </Text>

會被編譯成

React.createElement(
  Text,
  {style: styles.welcome},
  'Let us learn React.'
)

注意,使用JSX,一定要在scope中,能夠訪問到React和對應的Element。比如剛剛的例子,在程式碼的最上面看到了這樣的import

import React, { Component } from 'react';
import {
  //...
  Text,
} from 'react-native';

另外,JSX編譯結果可以線上檢視線上檢視

Tips:JSX本身也是Javascript expression。所以,可以將其賦值給變數,放到if和for中,當然作為引數傳入和返回值返回

如果你個Tag是空的,可以用/>進行close。比如

<CustomComponent style={styles.welcome}/>

大小寫

JSX對大小寫開頭是敏感的

  • 小寫字母開頭會被認為是html內建標籤。比如div
  • 大寫字母開頭會被認為是自己建立或者import的component。

所以,自定義的Components必須是大寫字母開頭的。

舉個例子:
如果上文的Text改成小寫,

<text style={styles.welcome}>
    Let us learn React.
 </text>

會被編譯成為

React.createElement(
  "text",
  {style: styles.welcome},
  'Let us learn React.'
)

React在解析的時候,會認為這和div類似,是html內建標籤,引起錯誤。

JS程式碼

JSX中的JS表示式要用大括號{/*這裡是JS程式碼*/}擴起來,不要加引號,加引號後React會認為它是字串。

比如,你可以這麼寫

<Text style={styles.welcome}>
   {"Let" + " us" + " learn" + " react"}
</Text>

點選Save,然後選中模擬器,command+R進行reload,仍然能夠正常顯示。

Children

這是本文最初的程式碼

<View style={styles.container}>
    <Text style={styles.welcome}>
      Let us learn react
    </Text>
</View>

可以看到,在JSX中可以巢狀Element形成一種層次結構。這種層次結構可以動態生成,比如

render() {
    var textElement = <Text style={styles.welcome}>Let us learn react</Text>
    return (
      <View style={styles.container}>
        {textElement}
      </View>
    );
  }

JSX更多的學習資料,方便想要深入學習的同學:

Element

Element是你在螢幕上想要看到的東西,在React中,一個element就是一個物件。

在React中,element是不可變的。就像電影一樣,由一幀一幀組成的,一個element就像電影中的一幀。如果想要使用者看到變化,那麼就渲染下一幀。

聰明的你可能會問,這樣效率不是很低嗎?

事實就是,React只會更新變化的那部分,對於不變的檢視,是不會重新渲染的。

React強調函數語言程式設計,不可變狀態是函數語言程式設計的核心思想之一。不可變狀態能夠讓你的程式碼更容易編寫,測試和維護。一個不可變的函式,在輸入一定的時候,輸出一定是一樣的。

Component

在React native開發中,Component是一個非常重要的概念。它類似iOS中的UIView或者安卓中的View,將檢視分成一個個的小部分。

React native中,我們通常採用ES6 class來定義一個Component。

比如上文的程式碼:

export default class ReactBasics extends Component {
    render(){
        //..
    }
}

其中,render是實際的渲染函式。通常,使用JSX來返回想要看到的檢視。

React Native中的Component都是原生的Component,通過JS bridge來呼叫原生的Component來渲染。

這些Component分為兩種:

  1. iOS/Android通用的。比如Navigator,Text,Image等等
  2. 平臺獨有的,比如NavigatorIOS,ProgressBarAndroid。

State/props

React的Component有兩個內建引數物件

  • props,有React自動初始化,包含了傳遞給一個Component的引數。
  • state,包含的引數物件應當用在render函式中,用作渲染。呼叫this.setState()會觸發上文提到的component重新渲染。

初始化

比如,我們對本文的程式碼進行修改,新建一個Component

class GreatingComponent extends Component{
  render(){
    return (
      <Text style={styles.welcome}>
          Greating from {this.props.name}
      </Text>
    );
  }
}

這個Component非常簡單:讀取this.props.name,然後作為Text顯示。

然後,我們使用這個Component,將ReactBasics修改成如下

export default class ReactBasics extends Component {
  render() {
    return (
      <View style={styles.container}>
        <GreatingComponent name={"Leo"}/>
      </View>
    );
  }
}

然後儲存檔案,選擇模擬器,command+R,重新載入。看到的介面如下:

通過這個例子,如何對Component初始化進行傳值已經很清楚了

  • <GreatingComponent name={"Leo"}/>,初始化的時候,通過JSX的引數來傳遞值
  • GreatingComponent內部,通過this.props.name來訪問這個值。

修改檢視狀態

React中,修改檢視狀態是通過this.setState觸發render重新呼叫,進而修改檢視的狀態。

我們繼續修改程式碼,新增一個建構函式,對state進行初始化,然後GreatingComponent初始化的時候,讀取this.state.name

在最上面的import中,新增一個TouchableOpacity,然後在點選事件中,呼叫this.setState更新顯示的文字。

export default class ReactBasics extends Component {
  constructor(props) {
   super(props);
   this.state = {name:"Tom"}
  }
  _onPressText(){
    this.setState({name:"Jack"});
  }
  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={()=>this._onPressText()}>
          <GreatingComponent name={this.state.name}/>
        </TouchableOpacity>
      </View>
    );
  }
}

然後,儲存,reload模擬器。點選下文字,效果如下。

setState注意事項

  • 不要直接修改state

//這樣並不會觸發重新渲染

this.state. name = 'Jack';
  • setState修改可能是非同步的。

React有可能會對多個this.setState進行收集,然後一起更新UI。所以,不要直接依賴於上一個狀態的結果。
所以,這樣是不對的

//此時上一個state可能還沒更新
this.setState({
  counter: this.state.counter + this.props.increment,
});

如果依賴於上一個狀態,使用this.setState第二個模式:

//這裡,把上一個state和當前引數作為輸入
this.setState(function(prevState, props) {
  return {
    counter: prevState.counter + props.increment
  };
});
  • setState是增量更新
    比如
export default class ReactBasics extends Component {
  constructor(props) {
   super(props);
   this.state = {firstName:"Tom", lastName:"Huang"}
  }
  _onPressText(){
    this.setState({firstName:"Jack"});
  }
  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={()=>this._onPressText()}>
          <GreatingComponent name={this.state.firstName +" " + this.state.lastName}/>
        </TouchableOpacity>
      </View>
    );
  }
}

可以看到

this.setState({firstName:"Jack"});

的效果,只是修改了firstName,lastName仍然保持不變。

Tips:上文onPress中的callBack採用了箭頭函式,除了箭頭函式,也可以使用function本身傳入

export default class ReactBasics extends Component {
  constructor(props) {
   super(props);
   this.state = {firstName:"Tom", lastName:"Huang"}
   this._onPressText = this._onPressText.bind(this);
  }
  _onPressText(){
    this.setState({firstName:"Jack"});
  }
  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity onPress={this._onPressText}>
          <GreatingComponent name={this.state.firstName +" " + this.state.lastName}/>
        </TouchableOpacity>
      </View>
    );

注意這一行

   this._onPressText = this._onPressText.bind(this);

因為JS中,class的函式預設沒有bound。需要呼叫bind來把this傳入_onPressText

生命週期

關於生命週期,在上一篇文章裡已經講過了。這裡再簡單帶過一筆

使用ES6的class來建立的component生命週期如下:

第一次顯示

  • componentWillMount 將要顯示
  • componentDidMount 已經顯示

呼叫this.setState

  • shouldComponentUpdate 是否要重新繪製
  • componentWillUpdate 將要重新繪製
  • componentDidUpdate

Component被移除

  • componentWillUnmount

後續

最近一段時間,部落格內容會集中在iOS進階,Swift和React Native三個方面。