ReactNativeアプリの作り方::Fetch APIとFlatList

このページの目標

  • [ ] Fetch APIを使ってHttpリクエストが扱えるようになる
  • [ ] FlatListを使ってリスト表示が出来るようになる
  • [ ] APIレスポンスをFlatListでリストを表示する実装が出来るようになる

Fetch API

Fetch APIとは

  • 現代のXMLHttpRequestのようなもの
  • WHATWGによって標準化され、ほとんどのモダンブラウザで利用可能
  • ReactNativeでも同様のインターフェースで使える

https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch

GET

const response = await fetch('https://example.com/images/1');

POST

const request = {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    'content-type': 'application/json',
  },
  body: JSON.stringify({image: base64String}),
};
const response = await fetch('https://example.com/images', request)

Responseを扱う

エラー時(4xx,5xx)も例外を吐かないので正常なやり取りが出来たか始めに確認する必要があります。

if(response.ok) { //200系が返ってきていることを確認
  return await response.json(); //レスポンス内容は非同期で受け取る
}

https://developer.mozilla.org/ja/docs/Web/API/Response

TypeScriptで型をつけてwrapする

FetchAPIはそのまま使うとanyを扱う必要があったり、クエリパラメータのエンコード機能がなかったりするので簡易的にWrapしたAPIClientを経由して利用します。

const API_ENDPOINT = 'https://exmaple.com'
type Params = { [key: string]: string | number };

class ApiClient {
  get = async <T>(path: string, params: Params = {}): Promise<T> => {
    const url = this.createUrl(path, params);
    const response = await fetch(url);
    if (response.ok) { 
      return (await response.json()) as T; 
    } else {
      const status = response.status;
      const body = await response.text();
      throw new Error(`status: ${status}, body: ${body}`);
    }
  };

  private createUrl(path: string, params: Params = {}): string {
    let url: string;
    if (Object.keys(params).length == 0) {
      const encoded_params = this.encoded_params(params);
      url = `${API_ENDPOINT}/${path}?${encoded_params.join('&')}`;
    } else {
      url = `${API_ENDPOINT}/${path}`;
    }
    return url;
  }

  private encoded_params(params: Params): string[] {
    return Object.keys(params).map(
      key => `${encodeURIComponent(key)}=${encodeURIComponent(String(params[key]))}`,
    );
  }
}
export default new ApiClient();

型を指定してGET

intreface Image {
  id: number;
  url: string;
  width: number;
  height: number;
}
const image = await = ApiClient.get<Image>(`images/${imageId}`);
return image.url;

FlatList

FlatListとは

  • ReactNative公式のリストを表示するのコンポーネント
  • 多機能でかつインターフェースが扱いやすい
  • FlatListのためにReactNativeが使いたくなるレベル

https://facebook.github.io/react-native/docs/flatlist

最小実装

<FlatList
  data={[{key: 'a'}, {key: 'b'}]} //データセットの提供
  renderItem={({item}) => <Text>{item.key}</Text>} //描画方法の指定
/>

APIからデータを受け取り表示

架空のAPIから過去のメッセージの一覧を取得する実装例です。ポイントは3点あります。

  • componentDidMountAPIリクエストする
  • 結果をStateに保存する
  • FlatListのデータソースはStateを参照する
interface Props {}
interface State {
  messages: Message[];
}

interface Message {
  id: number;
  text: string;
}

class App extends React.Component<Props, State> {
  //コンポーネントが描画されるタイミングで呼ばれるイベント
  async componentDidMount() { 
    const messages = await ApiClient.get<Message[]>('messages/1');
    this.setState({ messages }); // コンポーネントの状態を差分更新
  }
  render() {
    return (
      <FlatList
        keyExtractor={item => { return String(item.id);}}
        data={this.state.messages}
        renderItem={({ item }: { item: Message }) => <Text>{item.text}</Text>}
      />
    );
  }
}

FlatListの便利機能

ネイティブ実装だとかなり苦労するPull-to-Refreshや無限スクロールも数行で実装できます。今回は省略しますがヘッダー,フッター,複数カラムや横向きのスクロールなどで手軽に作成できます。

Pull-to-Refresh

      <FlatList
        keyExtractor={item => { return String(item.id);}}
        data={this.state.messages}
        renderItem={({ item }: { item: Message }) => <Text>{item.text}</Text>}
+       onRefresh={() => { handleOnRefresh(); }} //再読込処理を呼び出す
+       refreshing={this.state.refreshing} //リフレッシュ処理が走っているか状態をセット
      />

無限スクロール

      <FlatList
        keyExtractor={item => { return String(item.id);}}
        data={this.state.messages}
        renderItem={({ item }: { item: Message }) => <Text>{item.text}</Text>}
+       onEndReached ={() => { handleonEndReached(); }} //次ページのデータを読み込む
      />