ReactNativeのFlatListではまった話
背景
- FlatListのdataをstateで管理してリストの項目数を動的に変更しようとした
- だけどstateを更新しても画面は更新されない
うまくいかない状態
- うまくいかないサンプル
- 追加ボタンを押す度にlistに一件追加されるが表示に変化は起きない
App.js
import React, { Component } from 'react';
import { Button, FlatList, StyleSheet, Text, View } from 'react-native';
export default class App extends Component<{}> {
constructor(props) {
super(props);
// listの初期値は一件
this.state = { list: [{ key: new Date().toString() }] };
}
componentWillUpdate() {
// stateが更新される度にlistが更新されていることが確認できる
alert(JSON.stringify(this.state.list));
}
onPress() {
// listに一件追加している
const tmp = this.state.list;
tmp.push({ key: new Date().toString() });
this.setState({ list: tmp });
}
renderItem({ item }) {
return (
<View style={styles.itemContainer}>
<Text>{ item.key }</Text>
</View>
);
}
render() {
return (
<View style={styles.container}>
<Button title="追加" onPress={this.onPress.bind(this)} />
<FlatList data={this.state.list} renderItem={this.renderItem} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
flex: 1,
paddingHorizontal: 10,
paddingVertical: 25,
},
itemContainer: {
backgroundColor: 'lightblue',
paddingHorizontal: 30,
paddingVertical: 10,
margin: 5,
},
});
解決策
- FlatListにexecDataを渡してその値も毎回更新するようにするとうまくいく
App.js
import React, { Component } from 'react';
import { Button, FlatList, StyleSheet, Text, View } from 'react-native';
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
list: [{ key: new Date().toString() }],
listUpdate: 0, // 画面を再描画するためだけに使うstate
};
}
componentWillUpdate() {
alert(JSON.stringify(this.state.list));
}
onPress() {
const tmp = this.state.list;
tmp.push({ key: new Date().toString() });
this.setState({
list: tmp,
listUpdate: this.state.listUpdate + 1 // list更新のタイミングでこっちも更新
});
}
renderItem({ item }) {
return (
<View style={styles.itemContainer}>
<Text>{ item.key }</Text>
</View>
);
}
render() {
return (
<View style={styles.container}>
<Button title="追加" onPress={this.onPress.bind(this)} />
{/* execDataを追加 */}
<FlatList data={this.state.list} execData={this.state.listUpdate} renderItem={this.renderItem} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: 'white',
flex: 1,
paddingHorizontal: 10,
paddingVertical: 25,
},
itemContainer: {
backgroundColor: 'lightblue',
paddingHorizontal: 30,
paddingVertical: 10,
margin: 5,
},
});
解説
- FlatListの再描画はdataとexecDataのどちらかの値が「変化」したら行われるらしい
- ただし、その「変化」したの確認はlistの要素が変化したのは検知しないらしい
- だから確実に「変化」したと扱われるような値をexecDataにセットすると再描画される
あとがき
- コードレベルで追えてないので確認したら追記する