[React Native]FlatListのdataを変更しても画面が更新されない

# ReactNativeのFlatListではまった話

## 背景

- FlatListのdataをstateで管理してリストの項目数を動的に変更しようとした
- だけどstateを更新しても画面は更新されない

## うまくいかない状態

- うまくいかないサンプル
- 追加ボタンを押す度にlistに一件追加されるが表示に変化は起きない

![before.gif](https://qiita-image-store.s3.amazonaws.com/0/175213/f7443c56-589d-e413-951a-a804d721eefa.gif)

```js: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を渡してその値も毎回更新するようにするとうまくいく

![after.gif](https://qiita-image-store.s3.amazonaws.com/0/175213/fd12a7b0-d3e6-f496-447a-c5afca0d2e25.gif)


```js: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にセットすると再描画される

### あとがき

- コードレベルで追えてないので確認したら追記する