Flatlist内でCheckBoxを使って複数選択する方法
開発環境
- Expo SDK 42
- React Native Elements 3.4.2
実現したい動作
今回実装しようとした動作は↓動画に示したものです。
実際の動作は下のレポジトリのinstallation
の手順で試せます。
主要なモジュールと選考理由
今回はReact Native Elements
のCheckBox
を選びました。CheckBox
の候補としてはreact-native-bouncy-checkbox、react-native-checkbox等が上がるのですが、今回は
- yarnでパッケージ管理が可能
- IOS, Android の両者で問題なく動作
を確実に満たすモジュールとしてReact Native Elements
のものを選びました。
実装方法について
実装方法を検索したところ、主に二つの方法があることが分かりました。
- FlatListに渡っているJSONデータのid等を参照し、チェックマークを付けるデータを選択・再レンダーする方法
- 渡ってくるデータに
checked
などの項目を追加してchecked=true
となった時点で再レンダーする方法
一つ目の方法については下の動画が参考になると思います。
採用した方法についてのおおまかな説明
今回は上記二つ目の方法を採用しました。例えば下のようなデータを用意し、
[{
"時間割所属": "教育学部",
"開講": "前期",
"時間割コード": "M532001",
"科目": "生活構成研究",
"担当": "Teacher A",
}]
これにchecked:
の項目を追加します。
[{
"時間割所属": "教育学部",
"開講": "前期",
"時間割コード": "M532001",
"科目": "生活構成研究",
"担当": "Teacher A",
"checked": false,
}]
この処理は先ほどのGitHubレポジトリにあるApp.js
の12行目周辺で行っています。
// checked=falseを初期値として代入
getListData = async() => {
await d1_Data.forEach(value => value.checked = false);
console.log('データにfalseを追加')
setdata(d1_Data);
}
// マウント時のみ実行される
useEffect( () => {
getListData();
}, [])
CheckBoxをタップしたときにchecked=true/false
を切り替えるのですが、その処理はApp.js
の23行目周辺で行っています。
// 時間割コードが一致した要素に対してchecked=trueに変更
checkMark = (classId) => {
const classIdNumber = data.findIndex((id) => id.時間割コード == classId);
let newData = data;
newData[classIdNumber].checked = !newData[classIdNumber].checked ;
setdata(newData);
setisChecked(!isChecked);
}
renderItem = ({ item }) => (
<View style={styles.item}>
<View style={styles.wrapText}>
<Text>{item.科目}</Text>
<CheckBox
checked={item.checked}
onPress={() => checkMark(item.時間割コード)}
/>
</View>
</View>
);
最後に、FlatList側のextraData
にisChecked
を定めておき、isChecked
の値が変わるたびに再レンダーがかかるようにしています。具体的にはApp.js
の47行目周辺でこの処理を行っています。
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={item => item.時間割コード}
style={styles.list}
extraData={isChecked}
/>
問題点
上記の二つの方法を紹介しましたが、どちらにしてもisChecked
が変わるたびにFlatList内の全ての要素が再レンダーされるので動作が重くなってしまいます。
useMemo
フックを使えば割とましになると思います(気が向いたらそこまでやる)。