ここまでの記事で作ってきたニュースアプリに、今回はAPIで記事を取得するまでの間、ロード中であることが分かるようローディングマークを表示させる方法を紹介する。

ここまでの記事

  1. fetch APIでニュースアプリを作る
  2. ニュースアプリの記事リストにサムネイルを表示させる

ActivityIndicator

React Nativeには、ローディング中であることをユーザーに知らせるための専用APIとして「ActivityIndicator」というものが用意されている。

使い方

まずはAPIのインポート。

import { ActivityIndicator } from 'react-native';

次にロード中か、そうでないかを判断するためのstate変数を用意する。

ここまで作ってきたApp.jsのコンストラクタにisLoadingというstateを追加しよう。

constructor() {
  super();
  this.state = {
    isLoading: true,
    threads: [],
  }
}

ロード中の場合はtrue、ロードが完了した場合はfalseを返すものとして、初期値をtrueに設定しておく。

state変数の用意が出来たら、通信が完了した時点でstateを書き換えるよう、componentDidMount内のコードも修正する。

componentDidMount() {
  fetch("https://www.reddit.com/r/newsokur/hot.json")
    .then((response) => response.json())
    .then((responseJson) => {
      let threads = responseJson.data.children;
      threads = threads.map(i => {
        i.key = i.data.url;
        return i;
      });

      this.setState({threads: threads, isLoading: false});
    })
    .catch((error) => {
      console.error(error);
    })
}

次にisLoadingの値によって処理を分ける条件分岐を書いていく。

JSXで条件分岐を行う場合は三項演算子を使う必要がある。

{ isLoading ? <ActivityIndicator /> : /*通常処理*/ }

ここまでをApp.jsに反映させると次のようになる。

import React, { Component } from 'react';
import {
  Text, View, FlatList, Image, Dimensions, ActivityIndicator
} from 'react-native';

export default class App extends Component {
  constructor() {
    super();
    this.state = {
      isLoading: true,
      threads: [],
    }
  }

  componentDidMount() {
    fetch("https://www.reddit.com/r/newsokur/hot.json")
      .then((response) => response.json())
      .then((responseJson) => {
        let threads = responseJson.data.children;
        threads = threads.map(i => {
          i.key = i.data.url;
          return i;
        });

        this.setState({threads: threads, isLoading: false});
      })
      .catch((error) => {
        console.error(error);
      })
  }

  render() {
    const { threads, isLoading } = this.state;
    const { width } = Dimensions.get('window');

    return(
      <View style={{
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
      }}>
        {isLoading ? <ActivityIndicator /> :
        <FlatList
          data={threads}
          renderItem={({item}) => {
            return(
              <View style={{
                flex: 1,
                flexDirection: 'row',
                width: '100%'
              }}>
                <Image
                  style={{
                    width: 50,
                    height: 50
                  }}
                  source={item.data.thumbnail}
                />
                  <View style={{
                    flex: 1,
                    flexDirection: 'column'
                  }}>
                    <Text>{item.data.title}</Text>
                    <Text style={{color: '#ababab', fontSize: 10}}>{item.data.domain}</Text>
                  </View>
              </View>
            )
          }}
        />
        }
      </View>
    )
  }
}

expo startコマンドでアプリを実行すると、以下のようなローディングマークがしばらく表示された後に記事リストが表示される。