Swift版に続き、React Native版のRSSリーダーを作ってみましょう。React Native版ならjavascriptで実装でき、iOSでもAndroidでも動きます。
今回はシンプルにYahoo!トピックスの見出しのみを表示して、内容はSafariに飛ばして表示するようにしています。
プロジェクト作成
React Nativeのプロジェクトはコマンドで作成します。react-nativeコマンドのインストール等事前準備はこちらを参考にしてください。
$ react-native init ReactNativeRssReader
$ cd ReactNativeRssReader
$ react-native run-ios
シミュレータがさくっと起動して、テンプレートのアプリが立ち上がります。
App.jsを改修していきましょう。
RSSのパース
RSSのURLを指定します。今回はソースに直書きです。
const FEED_URL = 'https://news.yahoo.co.jp/pickup/rss.xml';
Javascriptには標準のXMLパーサーがありません。node-xml2jsが使いやすいのですが、これをReact Nativeで使えるようにしたreact-native-xml2jsを使ってみます。
$ yarn add react-native-xml2js
componentDidMountでRSSを取得して、stateに保持しておきます。
type Props = {}; type State = { feedItems: Array, }; export default class App extends Component<Props, State> { state = { feedItems: [], } componentDidMount() { fetch(FEED_URL) .then(res => res.text()) .then((xml) => { const parser = xml2js.Parser(); parser.parseString(xml, (err, result) => { this.setState({ feedItems: result.rss.channel[0].item }); }); }); }
これでstateのfeedItemsにニュース一覧が入ります。
flowで型を指定しておくとよいです。
type FeedItem = { link: Array<string>, title: Array<string>, }; type State = { feedItems: Array<FeedItem>, };
描画処理
stateに格納されたデータを使用して表示部分を作成します。データの取得と表示を切り離して考えられるのはReact Nativeの本当にいいところですね。Swift版だとデータを取得した後tableViewをreloadDataする必要がありました。
React Nativeは描画だけでUIに関してはほとんど何もないので、StyleSheetを使って自分で整えていかないといけません。NativeBaseのようなUIライブラリを使ってもいいですが。
それほどデータ量は多くないですが、任意のデータ量を扱うのでFlatListを使っておきましょう。任意のデータのリストの描画にはScrollView内にViewを並べると極端にパフォーマンスが落ちることがあります。特にAndroidの場合はクラッシュしやすいです。
こういうケースではFlatListやSectionListを使いましょう。
render() { return ( <View style={styles.container}> <FlatList ItemSeparatorComponent={() => <View style={styles.separator} />} data={this.state.feedItems} renderItem={renderItem} keyExtractor={item => item.link[0]} /> </View> ); }
あとは実際に描画するrenderItemを実装するだけです。
const renderItem = (params: { item: FeedItem }) => ( <TouchableOpacity onPress={() => Linking.openURL(params.item.link[0])}> <View style={styles.row}> <Text>{params.item.title[0]}</Text> </View> </TouchableOpacity> );
stylesを適切に記述すれば完成です。
Learning React Native: Building Native Mobile Apps with JavaScript
- 作者: Bonnie Eisenman
- 出版社/メーカー: O'Reilly Media
- 発売日: 2017/10/23
- メディア: Kindle版
- この商品を含むブログを見る
ソースコード
App.js
// @flow import React, { Component } from 'react'; import { FlatList, Linking, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; import xml2js from 'react-native-xml2js'; const FEED_URL = 'https://news.yahoo.co.jp/pickup/rss.xml'; const styles = StyleSheet.create({ container: { flex: 1, flexDirection: 'row', backgroundColor: '#FFF', paddingTop: 20, }, row: { flex: 1, height: 50, padding: 10, }, separator: { flex: 1, height: 1, backgroundColor: '#ddd', marginLeft: 10, }, }); type Props = {}; type FeedItem = { link: Array<string>, title: Array<string>, }; type State = { feedItems: Array<FeedItem>, }; const renderItem = (params: { item: FeedItem }) => ( <TouchableOpacity onPress={() => Linking.openURL(params.item.link[0])}> <View style={styles.row}> <Text>{params.item.title[0]}</Text> </View> </TouchableOpacity> ); export default class App extends Component<Props, State> { state = { feedItems: [], } componentDidMount() { fetch(FEED_URL) .then(res => res.text()) .then((xml) => { const parser = xml2js.Parser(); parser.parseString(xml, (err, result) => { this.setState({ feedItems: result.rss.channel[0].item }); }); }); } render() { return ( <View style={styles.container}> <FlatList ItemSeparatorComponent={() => <View style={styles.separator} />} data={this.state.feedItems} renderItem={renderItem} keyExtractor={item => item.link[0]} /> </View> ); } }