News APIのサービスを利用してRNでNews一覧を作る


いちいち自分でデータつくるの大変なのでNews APIのAPIを利用してReactNativeでNewsページを作ってみたい。
[この記事](https://qiita.com/kana-t/items/05d7d110ec04311574d6)を参考に、端折れるところは端折る。

## 完成イメージ


![スクリーンショット 2019-09-28 19.55.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/86d39580-e8ec-0373-532b-0b6f7d1037bb.png "スクリーンショット 2019-09-28 19.55.03.png")



## 準備

### News API

[News APIのサイト](https://newsapi.org)に行き、登録して、APIを利用するためのKeyを取得する。
せっかくなので日本語の情報をリクエストしてみます。

```
https://newsapi.org/v2/top-headlines?country=jp&apiKey=API_KEY
```

### モジュール

追加でインストールするモジュールは以下の２つ。

* react-native-elements（見た目のパーツ）
* moment（普通の時間を、a hour ageとかとして表示するために利用）

```bash
npm install --save react-native-elements
npm install --save moment
```

## News API考察

レスポンスのデータ構造は以下のようになっているみたい。
articlesを取得して、書く項目をLoopする感じ。

```js
{
    status: '',
    totalResults: 111,
    articles:[
        {
            source: {
                id: null,
                name: ''
            },
            auther: '',
            title: '',
            description: '',
            url: '',
            urlImage: '',
            publishedAt: '2019-09-20T06:19:00Z',
            content: ''
        },
    ]
}
```

## 実装

全部App.jsに書きたいところだが、記事のレイアウトだけArticle.jsとして外部ファイルにする。
/screens/Article.jsに置く。

### App.js

```js:App.js
import React from 'react';
import { StyleSheet, Text, View, FlatList, SafeAreaView } from 'react-native';

import Article from './screens/Article';

export default class App extends React.Component {

    state = {
        articles: [],
        refreshing: false,
    }

    getNews = async () => {
        this.setState({ refreshing: true });
        const url = "https://newsapi.org/v2/everything?q=bitcoin&from=2019-08-28&sortBy=publishedAt&apiKey={your api key}";
        try {
            const result = await fetch(url);
            const json = await result.json();
            // console.log(json);
            this.setState({
                articles: json.articles,
                refreshing: false
            });
        } catch (e) {
            this.setState({ refreshing: false });
            console.log(e);
        }
    }

    componentDidMount = () => { 
        this.getNews();
    }

    render() {
        // console.log(this.state.articles);
        return (
            <SafeAreaView style={{ flex: 1 }}>
                <FlatList
                    data={this.state.articles}
                    keyExtractor={item => item.url}
                    renderItem={({ item }) => <Article article={item} />}
                    onRefresh={() => this.getNews()}
                    refreshing={this.state.refreshing}
                />
            </SafeAreaView>
        );
    }
}

```

### Article.js

```js:Article
import React from 'react';
import { View, Linking, TouchableHighlight } from 'react-native';
import { Text, Button, Card, Divider } from 'react-native-elements';
import moment from 'moment';

export default class Article extends React.Component {
    render() {
        const { description, publishedAt, source, urlToImage, url } = this.props.article;
        const time = moment(publishedAt || moment.now()).fromNow();
        // const time = publishedAt;
        const defaultImage = "https://wallpaper.wiki/wp-content/uploads/2017/04/wallpaper.wiki-Images-HD-Diamond-Pattern-PIC-WPB009691.jpg";
        return (
            <TouchableHighlight
                useForeground
                onPress={() => Linking.openURL(url)}
            >
                <Card
                    image={{ uri: urlToImage || defaultImage }}
                >
                    <Text style={{ marginBottom: 10 }}>{description || 'Read more...'}</Text>
                    <Divider style={{ backgroundColor: "#dfe6e9" }} />
                    <View style={{ flexDirection: "row", justifyContent: 'space-around' }}>
                        <Text style={{ margin: 5, fontSize: 10 }}>{source.name.toUpperCase()}</Text>
                        <Text style={{ margin: 5, fontSize: 10 }}>{time}</Text>
                    </View>
                </Card>
            </TouchableHighlight>
        );
    }
}
```