1. 程式人生 > >React Native官方入門教程

React Native官方入門教程

官方教程主要是一步一步的介紹如何用ListView來顯示一條Json資料。

模擬資料

在編寫關於獲取遠端資料的程式碼之前,先讓我們用一些模擬資料,以便上手React Native。在Facebook(公司),我們的做法是一般在JS檔案的頂部宣告常量,下面接著就是一些import。當然你可以在index.ios.js或index..android.js(這裡考慮android)的任意位置增加下面的常量:

var MOCKED_MOVIES_DATA = [ {title: 'Title', year: '2015', posters: {thumbnail: 'http://i.imgur.com/UePbdph.jpg'
}}, ];

渲染一個movie

我們現在為這個movie渲染title,year和thumbnail。由於thumbnail在React Native中是一個Image元件,因此增加Image到下面React imports列表。

import React, {
  Component,
} from 'react';
import {
  AppRegistry,
  Image,
  StyleSheet,
  Text,
  View,
} from 'react-native';

接著修改render方法來渲染我們上面提到的(模擬)資料。

render() {
    var movie = MOCKED_MOVIES_DATA[0];
    return (
      <View
style={styles.container}>
<Text>{movie.title}</Text> <Text>{movie.year}</Text> <Image source=
{{uri: movie.posters.thumbnail}} /> </View> ); }

手機上開啟開發者選單Reload JS後你應該可以看到“2015”和下面的“Title”。注意,Image沒有被渲染上。這是因為我們沒有為其制定寬和高。這需要通過styles來實現。讓我們清除不在使用的styles再修改styles。

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  thumbnail: {
    width: 53,
    height: 81,
  },
});

同時,我們需要將style應用到Image元件上。

    <Image
       source={{uri: movie.posters.thumbnail}}
       style={styles.thumbnail}
    />

重新Reload JS,我們會看到下面的效果:
這裡寫圖片描述

增加一些樣式

上面渲染了我們的資料,現在讓我們使他看起來更好,我打算在圖片的右邊放一些文字,讓title變大並且居中。
這裡寫圖片描述
我們需要增加一個容器以便我們在水平方向上佈局我們的元件。

 return (
        <View style={styles.container}>
          <Image
            source={{uri: movie.posters.thumbnail}}
            style={styles.thumbnail}
          />
          <View style={styles.rightContainer}>
            <Text style={styles.title}>{movie.title}</Text>
            <Text style={styles.year}>{movie.year}</Text>
          </View>
        </View>
      );

沒有太多的改變,我們增加了一個容器包含所有的Text並把這些Text移動Image的下面(因為他們是在Image的右邊顯示)。然後我們看看style做的修改:

container: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },

我們使用FlexBox(彈性盒模型)來佈局-關於FlexBox去this great guide瞭解更多。
在上面的程式碼片段中,我們增加了flexDirection:’row’,這使得容器裡的子view在水平方向上佈局而不是在垂直方向上。
現在在Js style object中增加另一個style:

rightContainer: {
    flex: 1,
  },

這意味著rightContainer佔據父容器中Image以外的剩餘的空間,如果你感覺效果不明顯,可以給rightContainer增加一個backgroundColor並移除flex:1。你將會看到容器尺寸變成了能容納子view的最小尺寸。
簡單的文字樣式:

title: {
    fontSize: 20,
    marginBottom: 8,
    textAlign: 'center',
  },
  year: {
    textAlign: 'center',
  },

重新Reload JS,你將看到新的檢視
這裡寫圖片描述

取得真正的資料

從Rotten Tomatoes’s API是取得資料是真正的開始學習React Native,因此我們在這塊盡情暢遊。
在JS檔案頂部增加下面的常量,在請求資料的時候需要使用。

/**
 * For quota reasons we replaced the Rotten Tomatoes' API with a sample data of
 * their very own API that lives in React Native's Github repo.
 */
var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';

為我們的應用增加一些初始的狀態,我們可以判斷this.state.movies == null ,根據movies是否為null來決定movies data是否已經載入完成。當請求響應回來時,使用this.setState(movies:moviesData)來設定資料。在React類裡render方法上增加這些程式碼:

constructor(props) {
    super(props);
    this.state = {
      movies: null,
    };
  }

我們要在元件載入完成後傳送一個請求。componentDimMount是React components方法,React僅僅在元件載入完成後呼叫一次該方法。

componentDidMount() { this.fetchData(); }

現在再增加fetchData方法。這個方法用於獲取資料。你需要做的只是在解析了協議之後呼叫this.setState({movies:data}),因為React工作的方式是:setState方法會觸發render方法重新渲染,這時render方法裡this.setstate.movies將不再是null。注意,我們在方法鏈最好呼叫了done()方法,一般總是需要呼叫done()方,否則任何丟擲的異常你將都看不到。

fetchData() {
    fetch(REQUEST_URL)
      .then((response) => response.json())
      .then((responseData) => {
        this.setState({
          movies: responseData.movies,
        });
      })
      .done();
  }

現在修改render()方法,如果沒有movies data就渲染loading view否則渲染第一個movies(獲取到的json資料包含多個movies)。

render() {
    if (!this.state.movies) {
      return this.renderLoadingView();
    }

    var movie = this.state.movies[0];
    return this.renderMovie(movie);
  }

  renderLoadingView() {
    return (
      <View style={styles.container}>
        <Text>
          Loading movies...
        </Text>
      </View>
    );
  }

  renderMovie(movie) {
    return (
      <View style={styles.container}>
        <Image
          source={{uri: movie.posters.thumbnail}}
          style={styles.thumbnail}
        />
        <View style={styles.rightContainer}>
          <Text style={styles.title}>{movie.title}</Text>
          <Text style={styles.year}>{movie.year}</Text>
        </View>
      </View>
    );
  }

重新reload JS,你將看到“Loading movies…”直到響應返回,才會渲染從Rotten Tomatoes取得的第一條movie。
這裡寫圖片描述

ListView

現在修改應用程式,我們使用ListView元件來渲染所有的資料,而不僅僅顯示出第一條movie。
為什麼使用ListView更好,而不是僅僅渲染所有這些資料或者使用ScrollView?儘管React是快速的,但是渲染一個可能無限的元素列表是慢的。使用ListView渲染views,你僅僅展現一整個螢幕的元素,但是螢幕以外(看不到的)的views會從native view的層次結構上刪除。
首先:新增ListView到import中。

import React, {
  Component,
} from 'react';
import {
  AppRegistry,
  Image,
  ListView,
  StyleSheet,
  Text,
  View,
} from 'react-native';

接著修改render方法,一旦獲取資料就使用ListView渲染所有movie,而不是僅僅一條movie。

render() {
    if (!this.state.loaded) {
      return this.renderLoadingView();
    }

    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderMovie}
        style={styles.listView}
      />
    );
  }

dataSource是一個介面,ListView用以決定哪一行在更新過程中改變過。
注意我們使用this.state的dataSource。接下來增加一個空的dataSource到constructor裡面。現在我們正在給dataSource裡的資料排序,我們不應該再使用this.state.movies已避免資料排序兩次。我們使用boolean型別的屬性(this.state.loaded)來判斷資料是否載入完成。

constructor(props) {
    super(props);
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      loaded: false,
    };
  }

修改fetchData方法,相應地更新state值。

fetchData() {
    fetch(REQUEST_URL)
      .then((response) => response.json())
      .then((responseData) => {
        this.setState({
          dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
          loaded: true,
        });
      })
      .done();
  }

最後我們為ListView增加樣式。

listView: {
    paddingTop: 20,
    backgroundColor: '#F5FCFF',
  },

最終的效果:
這裡寫圖片描述
最終的程式碼:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */

import React, {
  Component,
} from 'react';
import {
  AppRegistry,
  Image,
  ListView,
  StyleSheet,
  Text,
  View,
} from 'react-native';

var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';

class AwesomeProject extends Component {
  constructor(props) {
    super(props);
    this.state = {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      loaded: false,
    };
  }

  componentDidMount() {
    this.fetchData();
  }

  fetchData() {
    fetch(REQUEST_URL)
      .then((response) => response.json())
      .then((responseData) => {
        this.setState({
          dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
          loaded: true,
        });
      })
      .done();
  }

  render() {
    if (!this.state.loaded) {
      return this.renderLoadingView();
    }

    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={this.renderMovie}
        style={styles.listView}
      />
    );
  }

  renderLoadingView() {
    return (
      <View style={styles.container}>
        <Text>
          Loading movies...
        </Text>
      </View>
    );
  }

  renderMovie(movie) {
    return (
      <View style={styles.container}>
        <Image
          source={{uri: movie.posters.thumbnail}}
          style={styles.thumbnail}
        />
        <View style={styles.rightContainer}>
          <Text style={styles.title}>{movie.title}</Text>
          <Text style={styles.year}>{movie.year}</Text>
        </View>
      </View>
    );
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  rightContainer: {
    flex: 1,
  },
  title: {
    fontSize: 20,
    marginBottom: 8,
    textAlign: 'center',
  },
  year: {
    textAlign: 'center',
  },
  thumbnail: {
    width: 53,
    height: 81,
  },
  listView: {
    paddingTop: 20,
    backgroundColor: '#F5FCFF',
  },
});

AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);