react-nativeで一人前のiOSアプリ開発者を目指す 第3回 本のリストを取得するアプリ

こんにちは、個別指導塾コミット塾長、AWESOME開発担当の船津です。
引き続きLearning React Nativeを使って勉強していきます。

amazon

今回は前回の内容を踏まえつつ、ListViewを使ったアプリを作っていきます。
NewYork TimesのAPIを利用して、ジャンルごとに本の一覧を取得するアプリになります。
APIkeyはhttps://developer.nytimes.com/から取得して下さい メールアドレスだけで取得出来ます。

 

やりたいことは…

  • 初期状態でも何らかの本の一覧が表示されている(今回はe-book-fictionの一覧)
  • 入力欄に入力するとほんの一覧が更新される
  • 存在しないカテゴリを選択すると、エラーページが表示される

 

用意するのは以下のファイルです。

index.ios.js アプリの登録用
BookList.js アプリの本体、ListViewを書きます
BookItem.js ListViewの中身を書きます

まずはreact-native init BookListをして新しいアプリの雛形を作ります。

早速 index.ios.js を見てみましょう

import React, { Component } from 'react';
import {
  AppRegistry
} from 'react-native';

var BookList = require('./BookList')
AppRegistry.registerComponent('BookList', () => BookList);

次にBookList.js

'use strict'

import React, { Component } from 'react';
import {
  StyleSheet,
  Text,
  View,
  Image,
  ListView,
  TextInput,

} from 'react-native';

var BookItem = require('./BookItem');
var API_KEY = 'hogehogehoge';
var API_STEM = 'https://api.nytimes.com/svc/books/v3/lists'

class BookList extends Component {
  constructor() {
    super();
    // データソースを定義し、初期値を与えます
    var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2 });
    this.state = {
      query: 'e-book-fiction',
      dataSource: ds.cloneWithRows([]),
      error: false
    };
    // onChangeアクションに対してthisをbindします。
    this.onChange = this.onChange.bind(this);
  }
  onChange(event){
    var query = event.nativeEvent.text;
    this.setState({
      query: query
    });
    this._refreshData(this.state.query);
  }
	//デバイスの立ち上がり時にのみ実行される関数です。初期クエリの'e-book-fiction'でデータを取得します。
  componentDidMount() {
    this._refreshData(this.state.query);
  }
	// APIでデータを取得する cloneWithRowsで配列を読み込ませます。
  _refreshData(query) {
    var endpoint = <code>MARKDOWN_HASH84f1883cbf8d62980fdf7a2691c1b5cbMARKDOWN_HASH</code>
    fetch(endpoint)
      .then((response) =&gt; response.json())
      .then((rjson) =&gt; {
        console.log(rjson);
        this.setState({
          dataSource: this.state.dataSource.cloneWithRows(rjson.results.books),
          error: false
        });
      })
      .catch((error) =&gt; {
        console.warn(error);
        this.setState({
          error: true
        });
      });
  }

	//エラー時に表示するページ
  _renderError() {
    return (
      &lt;View style={styles.container}&gt;
        &lt;View style={styles.queryContainer}&gt;
          &lt;Text&gt;
            No result for {this.state.query} Please input again
          &lt;/Text&gt;
          &lt;TextInput placeholder=&quot;Please input query&quot; style={[styles.query, styles.mainText]} returnKeyType=&quot;go&quot; // 入力が完了して、「go」ボタンを押した際に呼び出される関数を定義します。 onSubmitEditing={this.onChange}/&gt;
        &lt;/View&gt;
      &lt;/View&gt;
    );
  }
	//BookItem.jsでモジュール化したListViewを読み込みます
  _renderRow(rowData) {
      return &lt;BookItem coverURL={rowData.book_image} title={rowData.title} author={rowData.author}/&gt;;
  }
	// ヘッダー
  _renderHeader() {
    return (
      &lt;View style={styles.sectionDivider}&gt;
        &lt;Text style={styles.headingText}&gt;
          Best Seller List
        &lt;/Text&gt;
      &lt;/View&gt;
    );
  }
	// フッター
  _renderFooter() {
    return (
      &lt;View style={styles.sectionDivider}&gt;
        &lt;Text&gt;
          Data from New York Times Best sellers list.
        &lt;/Text&gt;
      &lt;/View&gt;
    );
  }
  
   // Viewを描画します
  render() {
    if(this.state.error){
      return this._renderError();
    }
    else {
      return (
        &lt;View style={styles.container}&gt;
          &lt;View style={styles.queryContainer}&gt;
            &lt;TextInput placeholder=&quot;Please input query&quot; style={[styles.query, styles.mainText]} returnKeyType=&quot;go&quot; onSubmitEditing={this.onChange}/&gt;
            &lt;Text style={styles.headingText}&gt;
              Search results for {this.state.query}
            &lt;/Text&gt;
          &lt;/View&gt;
          // リストビューのパラメータを設定します。enableEmptySectionsはそのうち指定しなくてもよくなるかも
          
          &lt;ListView enableEmptySections={true} dataSource={this.state.dataSource} renderRow={this._renderRow} renderHeader={this._renderHeader} renderFooter={this._renderFooter} /&gt;
        &lt;/View&gt;
      );
    }
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
    paddingTop: 24,
  },
  list: {
    flex: 1,
    flexDirection: 'row',
  },
  listContent: {
    flex: 1 ,
    flexDirection: 'column',
  },
  row: {
    flex: 1,
    fontSize: 24,
    padding: 42 ,
    borderWidth: 1,
    borderColor: '#dddddd',
  },
  sectionDivider: {
    padding: 8,
    backgroundColor: '#eeeeee',
    alignItems: 'center'
  },
  headingText: {
    flex: 1,
    fontSize: 24,
    alignSelf: 'center',
  },
  button: {
    flex: 1,
    padding: 10,
    borderColor: '#000',
    borderRadius: 4,
    borderWidth: 2,
    alignSelf: 'flex-end',
    textAlign: 'center',
  },
  mainText: {
    fontSize: 16,
    color: '#000',
    alignItems: 'center',
  },
  query: {
    alignSelf: 'center',
    marginTop: 30,
    width: 300 ,
    height: 30,
    borderWidth: 1,
    borderColor: '#000',
    borderRadius: 4,
    marginBottom: 20,
    padding:5,
  },
});

module.exports = BookList;

続いてBookItem.jsです

'use strict'

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


class BookItem extends Component {
	//入力値のvalidationを行います。ここではstringでないと入力出来ないようになっています。
  propTypes: {
    coverURL: React.propTypes.string.isRequired,
    author: React.propTypes.string.isRequired,
    title: React.propTypes.string.isRequired,
  }

  render() {
    return (
      <View style={styles.bookItem}>
        <Image style={styles.cover} source={{uri: this.props.coverURL}}/>
          <View style={styles.info}>
            <Text style={styles.author}>{this.props.author}</Text>
            <Text style={styles.title}>{this.props.title}</Text>
          </View>
      </View>
    )
  }
}

var styles = StyleSheet.create({
  bookItem: {
    flex: 1,
    flexDirection: 'row',
    backgroundColor: '#FFFFFF',
    borderBottomColor: '#AAAAAA',
    borderBottomWidth: 2,
    padding: 5
  },
  cover: {
    flex: 1,
    height: 150,
    resizeMode: 'contain'
  },
  info: {
    flex: 3,
    alignItems: 'flex-end',
    flexDirection: 'column',
    alignSelf: 'center',
    padding: 20
  },
  author: {
    fontSize: 18
  },
  title: {
    fontSize: 18,
    fontWeight: 'bold'
  }
});

//他のファイルから読み込めるようにexportします。
module.exports = BookItem;

完成するとこんな感じで表示されます。

 

react-nativeで一人前のiOSアプリ開発者を目指す 第3回 本のリストを取得するアプリ

 

一応期待通りの動作をするようになりましたが、カテゴリの判定が厳しいので、入力欄ではなく、セレクトボックスにするといった工夫をしても良かったかもしれません。

Instagram Feed

Load More

Instagram

専務取締役

funatsukeisuke

WEBと教育を組み合わせて何かおもしろいことをやってやろうと画策しています。AWESOMEでは開発を担当、個別指導塾コミットでは塾長と2足の草鞋を履きながら日々勉強しています。

他の投稿を見る →