FlatListの最適化
どうやって計測するか
もしくは、どの指標で計測するか
FlatListのpropsを使って改善する
removeClippedSubviews ref default: iosではfalse, androidではtrue
trueにするとviewport外のViewはNativie View階層から切り離され、スクロールのパフォーマンスが向上する
複雑なことをしている場合は、コンテンツの欠落などのバグが生じうる
default: 10
1回のスクロールごとに描画するアイテムの量
大きくすると表示的に空白になる箇所が減るがパフォーマンスが低下する
updateCellsBatchingPeriod ref バッチごとの描画の遅延msを指定する
上のmaxToRenderPerBatchと組み合わせる
長時間待って多くのアイテムを取得する、短時間ごとに小さなアイテムを取得する、などの調整ができる
最初のbatchで描画するアイテム数
これを調節して、画面に収まる分だけ描画するなど
default: 21
この値を小さくすると同時にmountするアイテム数が減り、メモリが節約される
デフォルトでかくね?mrsekut.icon
認識が間違っているのか
よくわからんmrsekut.icon
全てのアイテムの高さが同じ場合はこれを使おう
FlatListが非同期レイアウト計算をする必要がなくなる
keyExtractorを使う
一意のkeyを指定する
renderItemに渡すコンポーネントはシンプルにする
1箇所でのみ複雑に使われるなら、それのために新しいコンポーネントを作るべき
できるだけ小さいロジックで作る
複雑になるほど重くなる
重い画像は避ける
use as little information as possible in your list.
リストの情報はできるだけ使用しない、だと..
再描画を避ける工夫をする
renderItemに無名関数を使わない
code:ts
renderItem = ({ item }) => (<View key={item.key}><Text>{item.title}</Text></View>);
render(){
// ...
<FlatList
data={items}
renderItem={renderItem}
/>
// ...
}
extraData
アイテムの一つを更新するためにdataを更新すると、FlatList全て(?)が再描画されてしまう
なのでextraDataを更新する
アイテムがチェックされているかどうかなどは、FlatListを使っているコンポーネントが管理しないといけない
code:ts
const P = () => {
const C = () => <H onPress={setSectedId}></H> // 子
return (
<FlatList
renderItem={C}
extraData={selectedId}
/>
)
}
複数選択できるなら無理じゃんmrsekut.icon
code:ts
class SelectableItem extends React.Component {
constructor() {
super();
this.handleOnPress = this.handleOnPress.bind(this);
}
shouldComponentUpdate(nextProps) {
const { isSelected } = this.props;
return isSelected !== nextProps.isSelected;
}
handleOnPress() {
const { onPress } = this.props;
onPress();
}
render() {
const { isSelected, text } = this.props;
const textColor = isSelected ? 'blue' : 'black';
return (
<TouchableOpacity onPress={this.handleOnPress}>
<View>
<Text style={{ color: textColor }}>{text}</Text>
</View>
</TouchableOpacity>
);
}
}
class SelectList extends React.Component {
constructor() {
super();
this.handleOnPressItem = this.handleOnPressItem.bind(this);
this.state = {
selected: new Map(),
};
}
onPressItem(id) {
this.setState((state) => {
const selected = new Map(state.selected);
selected.set(id, !selected.get(id));
return { selected };
});
}
renderItem({ item }) {
const { selected } = this.state;
return (
<SelectableItem
id={item.id}
onPressItem={this.handleOnPressItem}
selected={!!selected.get(item.id)}
title={item.title}
/>
);
}
render() {
const { data } = this.props;
return (
<FlatList
data={data}
extraData={this.state}
keyExtractor={item => item.id}
renderItem={this.renderItem}
/>
);
}
}
まじ?FCの場合でも同じ様に?
renderItemをuseCallbackしてる
さらにrenderitemはmemoでwrap
code:ts
// render item function, outside from class's render()
const renderItem = ({ item }) => (<Text key={item.key}>{item.key}</Text>);
// we set the height of item is fixed
const getItemLayout = (data, index) => (
{length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
);
const items = key: 'a' }, { key: 'b'}, ...+400;
function render () => (
<FlatList
data={items}
renderItem={renderItem}
getItemLayout={getItemLayout}
initialNumToRender={5}
maxToRenderPerBatch={10}
windowSize={10}
/>
);
headerやfooterのコンポーネントも無名じゃない方がいいのか
参考
特に再描画について
FlatListはpropsのわたし方によってはかなり非効率になりうる気がする
プロパティが多いし、描画かどうかをconsoleで調べられないので気にしないがち
そんでもって利用頻度が高い
例えばonRefreshやonEndReachedをpropsで渡してい
ここはuseWhyDidUpdateで調べられないので、調査できていないmrsekut.icon
その割にアプリ内の多くの場所で使われているのでちゃんと調べておきたいmrsekut.icon*2
コンポーネントを渡している
同値だが再描画を防ぐ
全ての関数はuseCallbackする
keyExtractor, ListHeaderComponent, ListFooterComponent
keyExtractorもmemo化するのメンドすぎる..mrsekut.icon
styleは外部に移動させる
renderItemを無名関数で指定しない ref 全てのprop使った場合のexample見たいは😡
理想の状態を見たいわmrsekut.icon
renderItemって再描画されなくなるの?