React Nativeの FlatList のパフォーマンス改善の話 (React Native Meetup #10)

React Nativeの FlatList のパフォーマンス改善の話 (React Native Meetup #10)

author potpro(ぼとぷろ)
2020/07/22

React Native の FlatList とのパフォーマンス改善の話 (React Native Meetup #10)

React Native Meetup #10で行ったLTについての補足、こぼれ話の記事です。もう二カ月経ってしまった。

LTはこちらです。

List型View(FlatList)とのパフォーマンスとの戦い

ちなみにオンライン開催です。自分も九州から配信していました。問題なくて良かったです。

オンラインは初で、オンラインでLTやるのちょっと大変だなあとも感じましたけどね。

potproject/ikuradon の完全RFC/Hooks化

ikura2

potproject/ikuradon

前に作っていたMastodon用のクライアントアプリをそろそろちゃんとしなきゃなと思っていまして、コードベースをReact Function Component(RFC)とReact Hooksに全部書き直しました。

また、React Navigation v5にしたりデザインもちゃんとして、すごく今時っぽいデザインになったかと思います。

当時の記事はこちら。もうReact部分はほぼ全部変わっているので、もはや全く違うアプリになっているかもしれません。

React Native製Mastodonクライアント「ikuradon」をgithubで公開しました

Reduxでビジネスロジックは切り離して書いていたことにより、ほぼReact Component部分を新規で書いたにも関わらず、Reduxのreducerやactionなどのビジネスロジックは100%と言っていいくらい、同じコードを流用したままリニューアルすることが出来ました。

今回でReact Reduxな設計の恩恵を強く感じられたかも。ロジックが疎結合になっている設計だと、こういう時に非常に改修がしやすいということを実感できました。

また、Function ComponentやHooksはこれを作るまで全く触れてなかったのですが、使ってみると従来のClass Component型の辛いところ、主に冗長な書き方辛い所がすごく改善されていて、書いていて非常に楽しかったです。

実装的に何か処理が怪しいところもこれでかなり解消できたようにも思います。Reactも進化していますね。

React.memoとuseMemo

// React.memo
// https://ja.reactjs.org/docs/react-api.html#reactmemo
const MyComponent = React.memo(function MyComponent(props) {
  /* render using props */
});

// useMemo
// https://ja.reactjs.org/docs/hooks-reference.html#usememo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

この2つの関数はpropsをメモ化して、条件によりコンポーネントの再レンダリングを抑えることができます。

パフォーマンス改善に非常に頼もしい。このプロダクトを書いていた初期の頃には無く、そしてほしかった機能。React v16.6.0で実装されました。

特にReact Nativeの場合、レンダリングコストってかなり高い部類と思います。RNをやる上で一番気になるところでかつ、パフォーマンスが出ないところはプロダクションだとネイティブモジュールを作って呼び出す、みたいな話も聞きます。

ただ、自分は趣味で作っているレベルなので、わざわざネイティブを組み込むレベルはサポートが厳しいし開発の工数が上がってしまうことから採用していません。Expoも使えなくなっちゃいますからね。

またFlatListだけに限ると、どうやら1つの行をレンダリングすると毎回全ての行の長さを再計算したり全てを破棄して作り直しているという挙動をすると聞くのですが、実際コードを見ていないのでわからず。そうであれば、リスト数が増えると計算量がすごいことになるのは明白ですよね。

flatlist-performance-tipsを見てみると、シンプルかつ軽量なコンポーネントにした方が良いと書かれているので、元を正せばこんな使い方には向かないという話になります。

またレンダリングコストもそうですが、Reactの設計上、1つの変更で大量のコンポーネントを書き換えるようになっていくから、パフォーマンスが厳しくなっていく感じもします。

なので基本的に、再レンダリングする必要のないコンポーネントはrenderを発火させないようにすることが一番ですね。この2つはReactを使用したアプリ全般で使用できる関数でしょう。

現状の問題とか

今のアプリ、iOSだとかなりサクサクに感じるんですが、Androidだとちょっともっさりという感じがします。

でもこのもっさりはボタンを押したときの画面遷移がより強く感じられて、React Navigationのパフォーマンスがそんなによくない感じもします。やはりJSとネイティブの壁なんですかね・・・。

後全く関係ない話になりますが、React Native for Webを使用したWeb Supportというものが出来るようになりました。まだまだ開発段階とはいえ、現在でもデザインやサポートしていないコンポーネント(Modalとか)以外はちゃんと動きます。

ログインも出来ますので、上手く手直しをすればちゃんと動かせるような感じもあります。まあwebはまだ実験的な機能なので、正式にサポートする気はないですが。

これからもReact Nativeに関しては使っていきたいところですね。