React Nativeでリスト構造のビューを実装する際、基本的には ListView
を使います。
使っている中で地味につまづきがちなところを、今回は紹介したいと思います。
表示が遅い
少ない件数を扱っているときには気になりませんが、100件とかを一気に表示しようとすると、下記のような問題が起きてきます。
- 1行ずつパラパラと描画される
- 操作がブロックされてしまうため、レンダリングが終わるまで他のアクションが動作しない
パフォーマンスの改善には下記のプロパティを使うことができます。
プロパティ | 説明 | デフォルト |
---|---|---|
initialListSize | 初期レンダリングする行数を指定。 | 10 |
pageSize |
initialListSize で指定した行数の初期レンダリング後、一度にレンダリングする行数を指定。 |
1 |
scrollRenderAheadDistance | スクロールが発生する場合、先読みで行のレンダリングをしておく範囲をピクセル数で指定。 | 1000 |
pageSize
のデフォルトが 1
なので、1行ごとにレンダリングされてパラパラと表示されていたんですね。
initialListSize
と pageSize
に適切な数値を設定してレンダリングの回数を減らすだけでも、表示速度はかなり改善できます。
scrollRenderAheadDistance
は他と比べて調整しても効果を実感しにくいかもです。
数値の最適化は、動かしながら調整してみましょう。
リストに追加できない
下記のようにリストを定義しているとき、みなさんだったら、後からリストに要素を追加する関数をどのように実装するでしょうか。
constructor() {
this.rawArray = [
{ id: 1 },
{ id: 2 },
];
this.state = {
dataSource: ds.cloneWithRows(this.rawArray),
};
}
大体の方は、まず下記のように実装するのではないかと思います。
// NGパターン
addItem(item) {
const {rawArray} = this;
rawArray.push(item);
this.setState({
dataSource: ds.cloneWithRows(rawArray),
});
}
これだとリストが再評価されず、表示に反映されません。
再評価させるためには、リストのインスタンスを新たに作り、cloneWithRows
に渡す必要があります。
// OKパターン
addItem(item) {
const newArray = this.rawArray.slice(); // ここ注目。シャローコピーしている
newArray.push(item);
this.setState({
dataSource: ds.cloneWithRows(newArray),
});
this.rawArray = newArray;
}
これで表示に反映されます。
リスト要素の更新や、削除の場合にも同様の実装が必要なので要注意です。
参考
https://facebook.github.io/react-native/docs/performance.html#listview-initial-rendering-is-too-slow-or-scroll-performance-is-bad-for-large-lists
https://github.com/facebook/react-native/issues/4104