ReactNativeのより良いリスト、FlatListについて。これまでのListViewとの違い。

ReactNative0.43(2017-04-03)より、FlatListが新しいcomponentとして追加されました。

FlatListにはこれまでのListViewで解決できなかった問題にリーチできるように設計されてます。特にパフォーマンス周りの問題です。AndroidにおけるRecylcerViewの登場のような出来事だと思っています。

具体的に嬉しいのは`onViewableItemsChanged`の追加です。ListViewでは同等のプロパティがiOSのみでのサポートでしたが、FlatListではAndroidでも動くらしいです（まだiOSでしか見てないです）。画面に表示されている行(item)を取得するが出来るので、各種パフォーマンス改善に役立てる事が出来ます。

ListViewに過去バージョンごとに追加されていったapiについても一通り既に実装されていますが、セクションヘッダーはありません。セクションヘッダーが欲しいなら`<SectionList>`を使ってね、ということでした。

待望の`onViewableItemsChanged`は最後に書くとして、まずは既存アプリのListViewを書き換えてみたのでdiffをみてみましょう。

## ListViewとの違い

ListViewの場合

```xml

render() {
  return (
    <ListView
      dataSource={this.state.dataSource}
      renderRow={this._renderRow.bind(this)}
      renderHeader={this._renderHeader.bind(this)}
      renderFooter={this._renderFooter.bind(this)}
      enableEmptySections={true}
      onEndReachedThreshold={100}
      onEndReached={this._onEndReached.bind(this)}
      refreshing={this.state.refreshing}
      refreshControl={
        <RefreshControl
          refreshing={this.state.refreshing}
          onRefresh={this._reload.bind(this)}
        />
      }
    />
  )
}
```

FlatListの場合

```xml

render() {
  return (
    <FlatList
      data={this.state.dataSource}
      renderItem={this._renderItem.bind(this)}
      ListHeaderComponent={() => this._renderHeader.call(this)}
      ListFooterComponent={() => this._renderFooter.call(this)}
      onEndReachedThreshold={100}
      onEndReached={this._onEndReached.bind(this)}
      onViewableItemsChanged={this._onViewableItemsChanged.bind(this)}
      refreshing={this.state.refreshing}
      refreshControl={
        <RefreshControl
          refreshing={this.state.refreshing}
          onRefresh={this._reload.bind(this)}
        />
      }
    />
  )
}
```

全てのプロパティを使ってないですが、基本的な部分は使ってると思います。
違いがあるのは最初の４つのプロパティです。

#### dataSource => data

表示するデータを渡すプロパティ。
ListViewの時はdataSourceに変更を検知する`DataSource.cloneWithRows`関数を通したオブジェクトを渡してましたが、FlatListのdataにはarrayをそのまま渡します。ただし、各itemにkey propertyを持たせてね、ということでした。

```
data={[{key: 'a'}, {key: 'b'}]}
```

#### renderRow => renderItem

itemをrenderする関数を渡します。
RowではなくItemなのはFlatListは正確にはテーブルを表示するためのコンポーネントではないからだと思います。iOSでいうCollectionViewをイメージすると良いと思います。一行に何アイテム表示するかを設定する`numColumns`というプロパティもあります。

ちなみに渡されるパラメーターにも少し違いがありました。
以下のように書き換えました。

```
renderRow(item, index) {
  return <ItemComponent item={item} />
}

renderItem(data) {
  let { item, index } = data;
  return <ItemComponent item={item} />
}
```

#### renderHeader, renderFooter

HeaderとFooterはrenderする関数を渡すものからコンポーネント自体を渡すものに変わっているので、最初のコードにあるように書き換えました。

#### その他

それ以外は元よりScrollViewから移譲されているものであったりするので、そのままでいけました。

## パフォーマンス改善

`onViewableItemsChanged`を利用してレンダリングコストの改善を試してみます。
`onViewableItemsChanged`に以下のような関数を渡します。

```
_onViewableItemsChanged(info) {
  let { viewableItems, changed } = info;
  this.setState({viewableItemIndices: viewableItems.map((item) => item.index)})
}
```

表示されている行のindexをstateで管理できるようになりました。
次に`renderItem`を修正します。

```
renderItem(data) {
  let { item, index } = data;
  if(this.state.viewableItemIndices.indexOf(index) < 0) return <View style={{height: 800}}/>;
  return <ItemComponent item={item} />
}
```

要するに見えてないitemは代わりに空のViewをrenderしてしまおうという作戦です。
表示時とViewの大きさが変わるとガタガタするので適当なサイズを指定したViewを表示しましょう。
FlatListは`getItemLayout`にサイズの取得方法を指定することもできるようなので、そちらで指定しても良いかもしれません。

これで見えてないitemは空のViewが表示されるようになりました。
各itemのレンダリングコストが高いアプリならかなりの改善が見込めるのではないでしょうか。

## 夢が広がる

Listのパフォーマンスはみんな困ってたみたいで、
これまでもRecyclerView的なComponentを有志がオープンソースで作ろうとしているを何度か見ました。なので結構、万を持しての登場だと思います。

自身も[短歌アプリ](https://itunes.apple.com/lu/app/minnano-duan-ge-tou-gaokomyuniti/id675671254?l=fr&mt=8)で実験的に各行の文字がアニメーションするなどフリーダムな機能を作ってたはいいものの、各itemがヘビーなのでパフォーマンスの問題でリリースを見送ったりしてました。

![4月-18-2017 19-45-31.gif](https://qiita-image-store.s3.amazonaws.com/0/29793/7b7d0f49-e634-43d4-df3c-39d8ba93d0f2.gif)


FlatListにすることでサクサク動きました。実験なのでホントにリリースするかはやっぱり別の問題です＼(^o^)／

以上です。

## 参考

FlatList Document
https://facebook.github.io/react-native/docs/listview.html

FlatListについての公式blog
https://facebook.github.io/react-native/blog/2017/03/13/better-list-views.html

短歌をはじめたいなら「うたよみん」で
https://itunes.apple.com/lu/app/minnano-duan-ge-tou-gaokomyuniti/id675671254?l=fr&mt=8