React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

【編者按】本篇文章的作者是 Joyce Echessa——渥合數(shù)位服務(wù)創(chuàng)辦人吞琐,畢業(yè)于臺(tái)灣大學(xué),近年來專注于協(xié)助客戶進(jìn)行 App 軟體以及網(wǎng)站開發(fā)然爆。本篇文章中站粟,作者介紹通過 React Native 框架構(gòu)建一個(gè)示例應(yīng)用的開發(fā)過程,使得網(wǎng)絡(luò)技術(shù)和移動(dòng)開發(fā)碰撞出絢麗火花曾雕!

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用 (1)

在 render()函數(shù)中奴烙,使用 TabBarIOS 組件創(chuàng)建一個(gè)分頁列。別忘了添加你使用的組件到解構(gòu)賦值中剖张,否則以后調(diào)用都需要使用完整名稱切诀,比如 React.TabBarIOS。

我們創(chuàng)建了兩個(gè)分頁列項(xiàng)目搔弄。我們?yōu)槊恳粋€(gè)項(xiàng)目設(shè)置選中狀態(tài)幅虑,并定義一個(gè)該項(xiàng)目被點(diǎn)擊時(shí)所調(diào)用的函數(shù)。以精選標(biāo)簽為例顾犹,我們之前定義的 selectedTab 狀態(tài)為「featured」倒庵,那么 selected 設(shè)置為 true褒墨,否則將被設(shè)置為 false。對(duì)于搜索標(biāo)簽頁也一樣擎宝,需要檢查 selectedTab 是否為「search」郁妈。一旦項(xiàng)目的 selected 設(shè)置為true,將成為激活狀態(tài)標(biāo)簽绍申。我們用系統(tǒng)圖標(biāo)表示標(biāo)簽欄項(xiàng)目噩咪。

需要注意的是,我們使用的自定義組件標(biāo)簽极阅,和其他的組件一樣胃碾。例如,我們需要相應(yīng)的模塊涂屁,并將其分配給一個(gè)變量书在,你可以使用變量來調(diào)用模塊。結(jié)果如同組件類的 render()函數(shù)一樣拆又,成為文件代碼的一部分儒旬。提醒一下,作者習(xí)慣使用變量名作為各自的類名帖族,但這并不是必須栈源,你可以用你喜歡的名稱。

當(dāng)一個(gè)標(biāo)簽欄項(xiàng)目點(diǎn)擊時(shí)竖般,會(huì)調(diào)用在組件的 onPress 屬性中定義的回調(diào)函數(shù)甚垦。函數(shù)會(huì)為 selectedTab 屬性設(shè)置數(shù)值,這個(gè)屬性將最終確定哪個(gè)是活動(dòng)標(biāo)簽涣雕。

調(diào)用模擬器艰亮,按下 Command-R 重載該應(yīng)用。正如下圖所示挣郭。

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)
React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

添加導(dǎo)航欄

下一步迄埃,我們將添加一個(gè)導(dǎo)航欄,并將兩個(gè)文件添加到項(xiàng)目中兑障。這些都將作為相應(yīng)標(biāo)簽出現(xiàn)在導(dǎo)航堆棧的根視圖侄非。分別命名文件為 BookList.js 和 SearchBooks.js。

在 BookList.js 添加以下代碼流译。

'use strict';
 
var React = require('react-native');
 
var {
    StyleSheet,
    View,
    Component
   } = React;
 
var styles = StyleSheet.create({
 
});
 
class BookList extends Component {
    render() {
        return (
            <View>
        </View>             
        );
    }
}
 
module.exports = BookList;

在 SearchBooks.js 中添加以下代碼逞怨。

'use strict';
 
var React = require('react-native');
 
var {
    StyleSheet,
    View,
    Component
   } = React;
 
var styles = StyleSheet.create({
 
});
 
class SearchBooks extends Component {
    render() {
        return (
            <View>
        </View>             
        );
    }
}
 
module.exports = SearchBooks;

在這兩個(gè)文件中創(chuàng)建空白視圖模塊,并導(dǎo)出該模塊福澡。

按照以下代碼修改 Featured.js叠赦。

'use strict';
 
var React = require('react-native');
var BookList = require('./BookList');
 
var {
    StyleSheet,
    NavigatorIOS,
    Component
   } = React;
 
var styles = StyleSheet.create({
    container: {
        flex: 1
    }
});
 
class Featured extends Component {
    render() {
        return (
            <NavigatorIOS
                style={styles.container}
                initialRoute={{
            title: 'Featured Books',
            component: BookList
            }}/>            
        );
    }
}
 
module.exports = Featured;

以上代碼使用 NavigatorIOS 組件來構(gòu)造一個(gè)導(dǎo)航控制器。我們將其初始路徑設(shè)定為 BookList 組件(這意味著 BookList 為其根視圖)革砸,并設(shè)置導(dǎo)航欄上方的標(biāo)題除秀。
接著用以下代碼修改 Search.js窥翩。

'use strict';
 
var React = require('react-native');
var SearchBooks = require('./SearchBooks');
 
var {
    StyleSheet,
    NavigatorIOS,
    Component
   } = React;
 
var styles = StyleSheet.create({
    container: {
        flex: 1
    }
});
 
class Search extends Component {
    render() {
        return (
            <NavigatorIOS
                style={styles.container}
                initialRoute={{
            title: 'Search Books',
            component: SearchBooks
        }}/>            
        );
    }
}
 
module.exports = Search;

正如在 Featured.js 一樣,以上代碼創(chuàng)建導(dǎo)航控制器鳞仙,再設(shè)置其初始路徑,接著為它設(shè)置標(biāo)題笔时。

重載應(yīng)用棍好,你可以看到以下界面。

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)
React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

獲取并顯示數(shù)據(jù)

現(xiàn)在允耿,我們開始將數(shù)據(jù)添加到視圖中借笙。起初,我們用虛構(gòu)數(shù)據(jù)構(gòu)建視圖较锡,之后再從 API 獲取真實(shí)的數(shù)據(jù)业稼。

在 BookList.js 中其他變量聲明的文件頂部,添加以下代碼蚂蕴。

var FAKE_BOOK_DATA = [
    {volumeInfo: {title: 'The Catcher in the Rye', authors: "J. D. Salinger", imageLinks: {thumbnail: 'http://books.google.com/books/content?id=PCDengEACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api'}}}
];

如下圖所示修改解構(gòu)賦值低散,以添加更多組件。

var {
    Image,
    StyleSheet,
    Text,
    View,
    Component,
   } = React;

添加如下樣式骡楼。

var styles = StyleSheet.create({
    container: {
        flex: 1,
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
        padding: 10
    },
    thumbnail: {
        width: 53,
        height: 81,
        marginRight: 10
    },
    rightContainer: {
        flex: 1
    },
    title: {
        fontSize: 20,
        marginBottom: 8
    },
    author: {
        color: '#656565'
    }
});

如下圖所示熔号,修改 BookList 類。

class BookList extends Component {
    render() {
    var book = FAKE_BOOK_DATA[0];
        return (
            <View style={styles.container}>
                <Image source={{uri: book.volumeInfo.imageLinks.thumbnail}}
                            style={styles.thumbnail} />
                <View style={styles.rightContainer}>
                    <Text style={styles.title}>{book.volumeInfo.title}</Text>
                    <Text style={styles.author}>{book.volumeInfo.authors}</Text>
                </View>
            </View>
        );
    }
}

重新加載應(yīng)用鸟整,可以看到下圖界面引镊。


React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)
React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

在上面的代碼中,我們創(chuàng)建一個(gè) JSON 對(duì)象篮条,非常類似于從 API 調(diào)用的對(duì)象弟头。我們?yōu)橐槐緯膶?duì)象創(chuàng)建屬性和值。在類文件中涉茧,我們使用虛構(gòu)數(shù)據(jù)赴恨,只為了得到第一個(gè)元素,并用它來填充我們的視圖降瞳。我們使用圖像組件來加載圖像到視圖嘱支。需要注意的是,我們?cè)跇邮奖碇性O(shè)定其寬度和高度挣饥。如果在樣式表中指定圖像的尺寸除师,那么在視圖中將看不到圖像。

我們?yōu)槿萜髦付?flexDirection 為「row」的樣式扔枫。這樣的話汛聚,元素的子代也將繼承該風(fēng)格,默認(rèn)值是水平布局而不是縱向布局短荐。請(qǐng)注意倚舀,我們是如何在組件內(nèi)包裝其他組件的叹哭。在上面代碼中,主容器中有兩個(gè)子元素——圖像和視圖痕貌。視圖也有自己的子類——兩個(gè)文本組件风罩。

先布局圖像組件,然后再將視圖(rightContainer)水平放置在它旁邊舵稠。我們?yōu)?rightContainer 指定的 flex 風(fēng)格為1超升。這使得該視圖組件占據(jù)剩余空間,而不會(huì)遮擋圖像組件哺徊。如果你想看 flex 樣式的效果室琢,可以為 rightContainer 添加以下代碼。

backgroundColor: 'red'

重新加載應(yīng)用落追,你會(huì)看到空間被 rightContainer 樣式組件占滿盈滴。但它不會(huì)遮擋到其他組件。之所以沒有延伸到整個(gè)屏幕轿钠,是因?yàn)橥馊萜髟O(shè)定了留白巢钓,而圖片也設(shè)置了右邊界。

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)
React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

刪除 rightContainer 的 flex 設(shè)定疗垛,再重新加載 App「捅ǎ現(xiàn)在組件只會(huì)占據(jù)適應(yīng)其內(nèi)容的足夠空間。

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)
React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

如果將 thumbnail 和 rightContainer 的 flex 樣式設(shè)置為2继谚,它們將會(huì)占據(jù)同樣的寬度烈菌,比例為2:2(或者1:1)。你可以將其設(shè)置為任何需要的數(shù)值花履,比例會(huì)做出相應(yīng)的改變芽世。

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)
React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

你可以嘗試不同的比例以得到你想要的結(jié)果。讓我們回到之前為 rightContainer 添加紅色背景的那一步诡壁,繼續(xù)下面的步驟济瓢。

添加列表視圖

React Native 有一個(gè) ListView 組件,顯示數(shù)據(jù)的滾動(dòng)行——也就是 iOS 中的表視圖妹卿。

首先旺矾,修改解構(gòu)語句顯示我們添加的更多的組件。

var {
    Image,
    StyleSheet,
    Text,
    View,
    Component,
    ListView,
    TouchableHighlight
   } = React;

添加以下代碼到樣式表中夺克。

separator: {
       height: 1,
       backgroundColor: '#dddddd'
   }

添加以下構(gòu)造函數(shù)到 BookList 類箕宙。

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

最后添加以下函數(shù)。

componentDidMount() {
    var books = FAKE_BOOK_DATA;
    this.setState({
        dataSource: this.state.dataSource.cloneWithRows(books)
    });
   }

在構(gòu)造函數(shù)中铺纽,我們創(chuàng)建了一個(gè) ListView.DataSource 對(duì)象柬帕,并將其分配給 dataSource 屬性。DataSource 是一個(gè)接口,ListView 用它來確定在更新 UI 過程中哪些行發(fā)生了變化陷寝。我們提供了一個(gè)可以比較兩列是否相同的函數(shù)锅很,用于確定數(shù)據(jù)列表是否變化。

當(dāng)組件被加載到 UI 視圖時(shí)凤跑,會(huì)調(diào)用 componentDidMount()函數(shù)爆安。該函數(shù)一旦被調(diào)用,我們用數(shù)據(jù)對(duì)象中的數(shù)據(jù)來設(shè)置 datasource 屬性仔引。

你可以使用下面的代碼來修改 render()函數(shù)鹏控。

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

將下面的函數(shù)添加到 BookList 類。

renderBook(book) {
       return (
            <TouchableHighlight>
                <View>
                    <View style={styles.container}>
                        <Image
                            source={{uri: book.volumeInfo.imageLinks.thumbnail}}
                            style={styles.thumbnail} />
                        <View style={styles.rightContainer}>
                            <Text style={styles.title}>{book.volumeInfo.title}</Text>
                            <Text style={styles.author}>{book.volumeInfo.authors}</Text>
                        </View>
                    </View>
                    <View style={styles.separator} />
                </View>
            </TouchableHighlight>
       );
   }

以上代碼在 render()函數(shù)中創(chuàng)建了一個(gè) ListView 組件肤寝。這里的 datasource 屬性與之前設(shè)定的數(shù)值一致。然后調(diào)用 renderBook()函數(shù)顯示 ListView 中的各列數(shù)據(jù)抖僵。

在 renderBook()函數(shù)中鲤看,我們使用 TouchableHighlight 組件。這是一個(gè)包裝組件耍群,能讓視圖正確響應(yīng)點(diǎn)擊行為义桂。一旦點(diǎn)擊,該包裝組件的透明度就會(huì)降低蹈垢,可以允許底層顏色透過慷吊,使得視圖變暗或變色。這樣的話曹抬,如果你點(diǎn)擊一個(gè) ListView 行溉瓶,你會(huì)看到高亮色,就像之前設(shè)置的選擇表格視圖單元格時(shí)的響應(yīng)一樣谤民。我們?cè)诜蛛x器的底部添加一個(gè)樣式為 separator 的空視圖組件堰酿。這樣的設(shè)定下,視圖會(huì)出現(xiàn)一個(gè)灰色的水平線张足,便于分割每行項(xiàng)目触创。

重載該應(yīng)用,你會(huì)看到只有一個(gè)單元的表格視圖为牍。

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)
React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

現(xiàn)在將真實(shí)數(shù)據(jù)加載到應(yīng)用中哼绑。

從文件中移除 FAKE_BOOK_DATA 變量,并添加以下代碼碉咆。這是加載數(shù)據(jù)的網(wǎng)址抖韩。

var REQUEST_URL = 'https://www.googleapis.com/books/v1/volumes?q=subject:fiction';

修改解析聲明。

var {
    Image,
    StyleSheet,
    Text,
    View,
    Component,
    ListView,
    TouchableHighlight,
    ActivityIndicatorIOS
   } = React;

添加以下樣式設(shè)定疫铜。

listView: {
       backgroundColor: '#F5FCFF'
   },
   loading: {
       flex: 1,
       alignItems: 'center',
       justifyContent: 'center'
   }

用下面的代碼修改構(gòu)造函數(shù)帽蝶。我們?yōu)榻M件的狀態(tài)對(duì)象添加另一個(gè)屬性,用來判斷視圖是否成功加載。

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

按下列代碼修改 componentDidMount()函數(shù)励稳,并添加 fetchData()函數(shù)佃乘。 fetchData()將調(diào)用 Google 圖書 API,當(dāng)它響應(yīng)操作時(shí)驹尼,會(huì)將獲取的數(shù)據(jù)設(shè)置為 DataSource 屬性趣避,同時(shí)將 isLoading 設(shè)置為 true。

componentDidMount() {
       this.fetchData();
   }
 
   fetchData() {
       fetch(REQUEST_URL)
       .then((response) => response.json())
       .then((responseData) => {
           this.setState({
               dataSource: this.state.dataSource.cloneWithRows(responseData.items),
               isLoading: false
           });
       })
       .done();
   }

修改 render()函數(shù)并添加 renderLoadingView()新翎。我們添加一個(gè)檢查 isLoading程帕,如果它的值為 true,就回到由 renderLoadingView()返回的視圖地啰。這個(gè)視圖顯示活動(dòng)指示燈(一個(gè)轉(zhuǎn)盤)愁拭,以及「正在載入書籍...」的字樣。當(dāng)加載完成后亏吝,你應(yīng)該看到表中的圖書清單岭埠。

render() {
       if (this.state.isLoading) {
           return this.renderLoadingView();
       }
 
       return (
            <ListView
                dataSource={this.state.dataSource}
                renderRow={this.renderBook.bind(this)}
                style={styles.listView}
                />
        );
}  
    
renderLoadingView() {
    return (
        <View style={styles.loading}>
            <ActivityIndicatorIOS
                size='large'/>
            <Text>
                Loading books...
            </Text>
        </View>
    );
}

重新加載應(yīng)用,你會(huì)看到類似下圖的界面蔚鸥。

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)
React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

添加詳情視圖

如果你點(diǎn)擊表中的一個(gè)單元格惜论,單元格將會(huì)突出顯示,但不會(huì)響應(yīng)其它操作止喷。我們將添加一個(gè)詳情視圖馆类,以顯示選擇當(dāng)前書的詳細(xì)信息。

在項(xiàng)目中新建文件弹谁,并命名為 BookDetail.js乾巧。將下面的代碼貼在該文件中。

'use strict';
 
var React = require('react-native');
 
var {
    StyleSheet,
    Text,
    View,
    Component,
    Image
   } = React;
 
var styles = StyleSheet.create({
    container: {
        marginTop: 75,
        alignItems: 'center'
    },
    image: {
        width: 107,
        height: 165,
        padding: 10
    },
    description: {
        padding: 10,
        fontSize: 15,
        color: '#656565'
    }
});
 
class BookDetail extends Component {
    render() {
        var book = this.props.book;
        var imageURI = (typeof book.volumeInfo.imageLinks !== 'undefined') ? book.volumeInfo.imageLinks.thumbnail : '';
        var description = (typeof book.volumeInfo.description !== 'undefined') ? book.volumeInfo.description : '';
        return (
            <View style={styles.container}>
                <Image style={styles.image} source={{uri: imageURI}} />
                <Text style={styles.description}>{description}</Text>
            </View>
        );
    }
}
 
module.exports = BookDetail;

上述代碼的大部分內(nèi)容预愤,我們之前已經(jīng)討論過卧抗,這里不再贅述。之前沒接觸過的是 props 屬性鳖粟,其用途是獲取數(shù)據(jù)社裆。通過設(shè)置 props 屬性,將數(shù)據(jù)傳遞到這個(gè)類向图。在代碼中泳秀,我們先獲得數(shù)據(jù),隨后用數(shù)據(jù)填充視圖榄攀。

需要注意的是嗜傅,我們?cè)O(shè)置了容器的上邊界。如果不這樣的話檩赢,視圖將從屏幕的最頂部開始吕嘀,從而導(dǎo)致某些元素被導(dǎo)航欄遮擋。

在 BookList.js 中添加以下代碼。

var BookDetail = require('./BookDetail');

修改 BookList 類中 render()函數(shù)的 TouchableHighlight偶房。

<TouchableHighlight onPress={() => this.showBookDetail(book)}  underlayColor='#dddddd'>

上述代碼指定了當(dāng)某列書籍被點(diǎn)擊時(shí)響應(yīng)一個(gè)回調(diào)函數(shù)趁曼。粘貼下面的函數(shù)到該類。這將 BookDetail 視圖推送到導(dǎo)航堆棧中棕洋,并設(shè)置導(dǎo)航欄上的標(biāo)題可見挡闰。然后將該選中行有關(guān)的圖書對(duì)象傳遞給 BookDetail 類。

showBookDetail(book) {
       this.props.navigator.push({
           title: book.volumeInfo.title,
           component: BookDetail,
           passProps: {book}
       });
   }

重載該 App掰盘,你能看到當(dāng)前選中書籍的詳細(xì)信息摄悯。

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)
React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

搜索

現(xiàn)在已經(jīng)完成了精選標(biāo)簽的主從視圖,我們將繼續(xù)完善搜索選項(xiàng)卡愧捕,使用戶能夠利用 API 查詢想要的書籍奢驯。

打開 SearchBooks.js 并按下面的代碼修改。

'use strict';
 
var React = require('react-native');
var SearchResults = require('./SearchResults');
var {
    StyleSheet,
    View,
    Text,
    Component,
    TextInput,
    TouchableHighlight,
    ActivityIndicatorIOS
    } = React;
 
var styles = StyleSheet.create({
    container: {
        marginTop: 65,
        padding: 10
    },
    searchInput: {
        height: 36,
        marginTop: 10,
        marginBottom: 10,
        fontSize: 18,
        borderWidth: 1,
        flex: 1,
        borderRadius: 4,
        padding: 5
    },
    button: {
        height: 36,
        backgroundColor: '#f39c12',
        borderRadius: 8,
        justifyContent: 'center',
        marginTop: 15
    },
    buttonText: {
        fontSize: 18,
        color: 'white',
        alignSelf: 'center'
    },
    instructions: {
        fontSize: 18,
        alignSelf: 'center',
        marginBottom: 15
    },
    fieldLabel: {
        fontSize: 15,
        marginTop: 15
    },
    errorMessage: {
        fontSize: 15,
        alignSelf: 'center',
        marginTop: 15,
        color: 'red'
    }
});
 
class SearchBooks extends Component {
 
    constructor(props) {
        super(props);
        this.state = {
            bookAuthor: '',
            bookTitle: '',
            isLoading: false,
            errorMessage: ''
        };
    }
 
 
    render() {
        var spinner = this.state.isLoading ?
            ( <ActivityIndicatorIOS
                hidden='true'
                size='large'/> ) :
            ( <View/>);
        return (
            <View style={styles.container}>
                <Text style={styles.instructions}>Search by book title and/or author</Text>
                <View>
                    <Text style={styles.fieldLabel}>Book Title:</Text>
                    <TextInput style={styles.searchInput} onChange={this.bookTitleInput.bind(this)}/>
                </View>
                <View>
                    <Text style={styles.fieldLabel}>Author:</Text>
                    <TextInput style={styles.searchInput} onChange={this.bookAuthorInput.bind(this)}/>
                </View>
                <TouchableHighlight style={styles.button}
                                    underlayColor='#f1c40f'
                                    onPress={this.searchBooks.bind(this)}>
                    <Text style={styles.buttonText}>Search</Text>
                </TouchableHighlight>
                {spinner}
                <Text style={styles.errorMessage}>{this.state.errorMessage}</Text>
            </View>
        );
    }
 
    bookTitleInput(event) {
        this.setState({ bookTitle: event.nativeEvent.text });
    }
 
    bookAuthorInput(event) {
        this.setState({ bookAuthor: event.nativeEvent.text });
    }
 
    searchBooks() {
        this.fetchData();
    }
 
    fetchData() {
 
        this.setState({ isLoading: true });
 
        var baseURL = 'https://www.googleapis.com/books/v1/volumes?q=';
        if (this.state.bookAuthor !== '') {
            baseURL += encodeURIComponent('inauthor:' + this.state.bookAuthor);
        }
        if (this.state.bookTitle !== '') {
            baseURL += (this.state.bookAuthor === '') ? encodeURIComponent('intitle:' + this.state.bookTitle) : encodeURIComponent('+intitle:' + this.state.bookTitle);
        }
 
        console.log('URL: >>> ' + baseURL);
        fetch(baseURL)
            .then((response) => response.json())
            .then((responseData) => {
                this.setState({ isLoading: false});
                if (responseData.items) {
 
                    this.props.navigator.push({
                        title: 'Search Results',
                        component: SearchResults,
                        passProps: {books: responseData.items}
                    });
                } else {
                    this.setState({ errorMessage: 'No results found'});
                }
            })
            .catch(error =>
                this.setState({
                    isLoading: false,
                    errorMessage: error
                }))
            .done();
    }
 
}
 
module.exports = SearchBooks;

述代碼中次绘,我們?cè)跇?gòu)造函數(shù)中設(shè)置了一些屬性:bookAuthor瘪阁、bookTitle、isLoading 和 errorMessage断盛。下面簡要介紹下如何使用。

在 render()方法中愉舔,我們需要檢查 isLoading 值是否為 true钢猛,如果是則建立一個(gè)活動(dòng)指示器,否則則創(chuàng)建一個(gè)空視圖(后面會(huì)用到)轩缤。然后命迈,我們創(chuàng)建一個(gè)被用來插入查詢的搜索表單。TextInput 用于接收輸入火的。當(dāng)組件的值改變時(shí)(例如用戶鍵入一些文本)壶愤,將會(huì)調(diào)用 TextInput 組件,同時(shí)為組件指定一個(gè)回調(diào)函數(shù)馏鹤。在調(diào)用時(shí)征椒,回調(diào)函數(shù) bookTitleInput()和 bookAuthorInput()利用用戶輸入的數(shù)據(jù)將設(shè)置 bookAuthor 和 bookTitles 屬性。當(dāng)用戶按下搜索按鈕時(shí)湃累,searchBooks()被調(diào)用勃救。需要注意的是,React Native 沒有按鈕組件治力。所以蒙秒,我們使用 TouchableHighlight 來代替,并用文本包裝宵统,使它的樣式看起來像一個(gè)按鈕晕讲。當(dāng)按下搜索按鈕時(shí),根據(jù)輸入的數(shù)據(jù)構(gòu)成一個(gè) URL。用戶可以通過書名瓢省、作者或書名+作者進(jìn)行搜索弄息。如果結(jié)果成功返回,SearchResult 將被推到導(dǎo)航堆棧净捅,否則提示錯(cuò)誤消息疑枯。我們還將響應(yīng)數(shù)據(jù)傳遞給 SearchResults 類。

創(chuàng)建一個(gè)文件并命名為 SearchResults.js蛔六,將下列代碼貼進(jìn)去荆永。

'use strict';
 
var React = require('react-native');
var BookDetail = require('./BookDetail');
var {
    StyleSheet,
    View,
    Text,
    Component,
    TouchableHighlight,
    Image,
    ListView
    } = React;
 
var styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center'
    },
    title: {
        fontSize: 20,
        marginBottom: 8
    },
    author: {
        color: '#656565'
    },
    separator: {
        height: 1,
        backgroundColor: '#dddddd'
    },
    listView: {
        backgroundColor: '#F5FCFF'
    },
    cellContainer: {
        flex: 1,
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
        padding: 10
    },
    thumbnail: {
        width: 53,
        height: 81,
        marginRight: 10
    },
    rightContainer: {
        flex: 1
    }
});
 
class SearchResults extends Component {
 
    constructor(props) {
        super(props);
 
        var dataSource = new ListView.DataSource(
            {rowHasChanged: (row1, row2) => row1 !== row2});
        this.state = {
            dataSource: dataSource.cloneWithRows(this.props.books)
        };
    }
 
    render() {
 
        return (
            <ListView
                dataSource={this.state.dataSource}
                renderRow={this.renderBook.bind(this)}
                style={styles.listView}
                />
        );
    }
 
    renderBook(book) {
        var imageURI = (typeof book.volumeInfo.imageLinks !== 'undefined') ? book.volumeInfo.imageLinks.thumbnail : '';
 
        return (
            <TouchableHighlight onPress={() => this.showBookDetail(book)}
                                underlayColor='#dddddd'>
                <View>
                    <View style={styles.cellContainer}>
                        <Image
                            source={{uri: imageURI}}
                            style={styles.thumbnail} />
                        <View style={styles.rightContainer}>
                            <Text style={styles.title}>{book.volumeInfo.title}</Text>
                            <Text style={styles.author}>{book.volumeInfo.authors}</Text>
                        </View>
                    </View>
                    <View style={styles.separator} />
                </View>
            </TouchableHighlight>
        );
    }
 
    showBookDetail(book) {
 
        this.props.navigator.push({
            title: book.volumeInfo.title,
            component: BookDetail,
            passProps: {book}
        });
    }
 
}
 
module.exports = SearchResults;

以上代碼之前已經(jīng)討論過,也不再贅述国章。代碼中獲得數(shù)據(jù)具钥,并將數(shù)據(jù)通過 props 屬性傳遞到類,同時(shí)創(chuàng)建填充了數(shù)據(jù)的 ListView液兽。

作者注意到骂删,在 API中,當(dāng)你按作者搜索時(shí)四啰,結(jié)果不一定是書的數(shù)據(jù)宁玫,而是作者自身的信息。這意味著某些行的 book.volumeInfo.imageLinks.thumbnail 和 book.volumeInfo.description 有未定義的值柑晒。所以我們稍作檢查欧瘪,如果沒有圖像則顯示一個(gè)空視圖。否則匙赞,我們的應(yīng)用將試圖加載不存在的圖像佛掖,這樣會(huì)容易引發(fā)崩潰。

我們使用之前用過的 BookDetail 組件涌庭,來顯示每本書的詳細(xì)信息芥被。如圖所示,打開 BookDetail.js 并修改 render()函數(shù)坐榆。在用數(shù)據(jù)填充視圖之前拴魄,檢查傳入的數(shù)據(jù)是否有相關(guān)圖像和詳細(xì)信息。如果嘗試載入的書籍沒有詳情或圖片席镀,對(duì)應(yīng)的區(qū)域?qū)⑹强瞻赘ΑD憧梢韵蛴脩籼崾疽粋€(gè)錯(cuò)誤信息,在此我們省略該步驟愉昆。

render() {
    var book = this.props.book;
    var imageURI = (typeof book.volumeInfo.imageLinks !== 'undefined') ? book.volumeInfo.imageLinks.thumbnail : '';
    var description = (typeof book.volumeInfo.description !== 'undefined') ? book.volumeInfo.description : '';
    return (
        <View style={styles.container}>
            <Image style={styles.image} source={{uri: imageURI}} />
            <Text style={styles.description}>{description}</Text>
        </View>
    );
}

重載應(yīng)用职员,會(huì)看到搜索書籍的界面。

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)
React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用(2)

結(jié)束語

盡管仍在不斷完善跛溉,React Native 看起來很有希望成為構(gòu)建移動(dòng)應(yīng)用的另一種選擇焊切。它為 Web 開發(fā)人員開啟了一扇門扮授,讓他們能夠在移動(dòng)開發(fā)領(lǐng)域一探究竟。同時(shí)為移動(dòng)開發(fā)者提供了一種簡化開發(fā)流程的新方式专肪。

隨著項(xiàng)目的發(fā)展刹勃,讓我們拭目以待 React Native 和應(yīng)用開發(fā)(iOS和Android ——或者別的平臺(tái))將會(huì)碰撞出什么樣的火花。同時(shí)嚎尤,如果你需要進(jìn)一步確認(rèn)網(wǎng)絡(luò)技術(shù)是否能用于實(shí)現(xiàn)真正的原生體驗(yàn)荔仁,你可以看看這些由 React Native 構(gòu)建的應(yīng)用:Facebook Ads Manager(完全由 React Native 構(gòu)建)以及 Facebook Groups(React Native 和 Objective-C 構(gòu)建的混合應(yīng)用)。

「學(xué)習(xí)一次芽死,在任何地方應(yīng)用」乏梁。單這一句足以證明學(xué)習(xí) React Native 框架的意義。

你可以在這里下載完整示例項(xiàng)目关贵。

為了更進(jìn)一步了解 React Native遇骑,你可以參考下列視頻和資料。

你可以在這下載 Xcode 項(xiàng)目揖曾,僅供參考落萎。(完結(jié))

React Native 簡介:用 JavaScript 搭建 iOS 應(yīng)用 (1)

原文地址:http://www.appcoda.com/react-native-introduction/

OneAPM 是應(yīng)用性能管理領(lǐng)域的新興領(lǐng)軍企業(yè),能幫助企業(yè)用戶和開發(fā)者輕松實(shí)現(xiàn):緩慢的程序代碼和 SQL 語句的實(shí)時(shí)抓取炭剪。想閱讀更多技術(shù)文章练链,請(qǐng)?jiān)L問 OneAPM 官方博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奴拦,一起剝皮案震驚了整個(gè)濱河市媒鼓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粱坤,老刑警劉巖隶糕,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓷产,死亡現(xiàn)場(chǎng)離奇詭異站玄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)濒旦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門株旷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人尔邓,你說我怎么就攤上這事晾剖。” “怎么了梯嗽?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵齿尽,是天一觀的道長。 經(jīng)常有香客問我灯节,道長循头,這世上最難降的妖魔是什么绵估? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮卡骂,結(jié)果婚禮上国裳,老公的妹妹穿的比我還像新娘。我一直安慰自己全跨,他們只是感情好缝左,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浓若,像睡著了一般渺杉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上七嫌,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天少办,我揣著相機(jī)與錄音,去河邊找鬼诵原。 笑死英妓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绍赛。 我是一名探鬼主播蔓纠,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吗蚌!你這毒婦竟也來了腿倚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤蚯妇,失蹤者是張志新(化名)和其女友劉穎敷燎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體箩言,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硬贯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陨收。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饭豹。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖务漩,靈堂內(nèi)的尸體忽然破棺而出拄衰,到底是詐尸還是另有隱情,我是刑警寧澤饵骨,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布翘悉,位于F島的核電站,受9級(jí)特大地震影響居触,放射性物質(zhì)發(fā)生泄漏妖混。R本人自食惡果不足惜包吝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望源葫。 院中可真熱鬧诗越,春花似錦、人聲如沸息堂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荣堰。三九已至床未,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間振坚,已是汗流浹背薇搁。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渡八,地道東北人啃洋。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像屎鳍,于是被迫代替她去往敵國和親宏娄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容