【React Native】FlatListを使ってTab Barを作る


自分用の備忘録です（編集中の箇所あり）

＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝
以下の動画に沿って React Native で Tab Bar の作成したので、手順をまとめます。
[React Native: Custom Tab Radio & Filter List Data](https://www.youtube.com/watch?v=QWZbifSLQsA)



####インポート
```react
import React from "react";
import {
 Text,
 StyleSheet,
 View,
 Image,
 TextInput,
 Dimensions,
 FlatList,
 TouchableOpacity,
 TouchableOpacityComponent,
} from "react-native";
```

## Step1 ： タブボタンを作る
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1675687/74c83d5d-af30-420f-45a9-8e5ca57bc3fe.png">

```react
const Practice = () => {
  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.listTab}>
        <TouchableOpacity style={styles.btnTab}>
         <Text style={styles.textTab}>All</Text>
        </TouchableOpacity>
      </View>
    </SafeAreaView>
  );
};
export default Practice;
```
CSS

```react
const styles = StyleSheet.create({
  container: {
    justifyContent: "center",
    backgroundColor: "#fff",
    padding: 15,
  },

  listTab: {
    flexDirection: "row",
    alignSelf: "center",
    marginBottom: 20,
  },
  btnTab: {
    width: Dimensions.get("window").width / 3,
    flexDirection: "row",
    borderWidth: 0.5,
    borderColor: "#ebebeb",
    padding: 10,
    justifyContent: "center",
  },
  textTab: {
    fontSize: 16,
  },
});
```


## Step2 ： タブボタンを複数にする
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1675687/9280ea38-d4a2-789c-f50c-076eb4cc9ff7.png">


#### ① タブボタンの定義

```react
const listTab = [
  {
    status: "All",
  },
  {
    status: "Purple",
  },
  {
    status: "Green",
  },
];
```
#### ② 定義したタブボタンをループで表示
```react
const Practice = () => {
  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.listTab}>
    //配列をループ表示　 ここから
        {
        listTab.map((e) => (
          <TouchableOpacity style={styles.btnTab}>
            <Text style={styles.textTab}>{e.status}</Text>
          </TouchableOpacity>
        ))
        }
　　　　　　　 //ここまで
      </View>
    </SafeAreaView>
  );
};
```

`.map` で配列要素をループで表示することができます


##Step３ ： ボタンをクリックした時にボタンの色を変化させる（状態変化）
背景がピンク、文字が白になるようにします。
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1675687/589f7663-30ce-319a-6526-c460b94969b0.png">


`useState`を使用

>Stateは コンポーネントの状態を表すデータ です。状態ですので、状況によって値が変化します。
>[Stateでコンポーネントの状態を管理する](https://dev.classmethod.jp/articles/react-native-state/)

>useState によって React の state の機能を関数コンポーネントに追加できます。
>[React](https://ja.reactjs.org/docs/hooks-state.html)

#### ① useStateをインポート

```react
import React, { useState } from "react";
```
#### ② stateの初期値を設定

```react
const Practice = () => {
  //初期値を"All"にセット ここから
  const [status, setStatus] = useState("All");
  //ここまで
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.listTab}>
        {listTab.map((e) => (
          <TouchableOpacity 
          style={styles.btnTab}>
            <Text style={styles.textTab}>{e.status}</Text>
          </TouchableOpacity>
        ))}
      </View>
    </SafeAreaView>
  );
};
```
>useStateでは、現在の state（状態） と、それを更新するための関数とを、ペアにして返します。
上記の`const [status, setStatus]` の場合、`status`という名前の state 変数を宣言しており、初期値ではそれを"All"にセットしています。現在の status の値を更新したい場合は、setStatus を呼ぶことができます。

参考：[React](https://ja.reactjs.org/docs/hooks-state.html)

#### ③ ボタンにイベントを設定
`onPress`でイベントを設定

```react
const Practice = () => {
  const [status, setStatus] = useState("All");
//ファンクションを定義（内容はStep5で）　ここから
  const setStatusFilter = (status) => {
    setStatus(status);
//ここまで
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.listTab}>
        {listTab.map((e) => (
          <TouchableOpacity
            style={styles.btnTab}
//[onPress] クリックするとイベント（setFunctionFilter）が発生　　ここから
            onPress={() => setStatusFilter(e.status)}
//ここまで
          >
            <Text style={styles.textTab}>{e.status}</Text>
          </TouchableOpacity>
        ))}
      </View>
    </SafeAreaView>
  );
};
```
`setFunctionFilter`のファンクションの中身は後で作ります

#### ④ ボタンをクリックした時に色が変化するよう設定

```react  
return (
    <SafeAreaView style={styles.container}>
      <View style={styles.listTab}>
        {listTab.map((e) => (
          <TouchableOpacity
          //クリックしたボタンにbtnTabActiveのスタイルが適用される、背景ピンクになる ここから
            style={[styles.btnTab, status === e.status && styles.btnTabActive]}
          //ここまで
            onPress={() => setStatusFilter(e.status)}
          >
            <Text
            //クリックしたボタンにbtnTextActiveのスタイルが適用される、文字が白になる ここから
              style={[
                styles.textTab,
                status === e.status && styles.textTabActive,
              ]}
            //ここまで
            >
              {e.status}
            </Text>
          </TouchableOpacity>
        ))}
      </View>
    </SafeAreaView>
  );
};
```

CSS

```react
  btnTabActive: {
    backgroundColor: "#e6838d",
  },

  textTabActive: {
    color: "#fff",
  },
```

##Step4 ： リストを表示する
アイテムリストを並べただけ（ボタンを押してもまだ表示は変わりません）
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1675687/23be9891-dafd-a22e-fbc4-942f9a0b01f3.png">

`FlatList`を使用

#### ① リストデータを作成

```react
const listTab = [
  {
    status: "All",
  },
  {
    status: "Purple",
  },
  {
    status: "Green",
  },
];

//データ作成 ここから
const data = [
  {
    name: "Ronaldo",
    status: "Green",
  },
  {
    name: "Messi",
    status: "Purple",
  },
  {
    name: "Kaka",
    status: "Green",
  },
  {
    name: "Mbappe",
    status: "Green",
  },
  {
    name: "Lukakau",
    status: "Purple",
  },
];
//ここまで
```

#### ② リストを表示

```react
const Practice = () => {
  const [status, setStatus] = useState("All");
  const setStatusFilter = (status) => {
    setStatus(status);
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.listTab}>
        {listTab.map((e) => (
          <TouchableOpacity
            style={[styles.btnTab, status === e.status && styles.btnTabActive]}
            onPress={() => setStatusFilter(e.status)}
          >
            <Text
              style={[
                styles.textTab,
                status === e.status && styles.textTabActive,
              ]}
            >
              {e.status}
            </Text>
          </TouchableOpacity>
        ))}
      </View>

     //FlatList ここから
      <FlatList
        data={datalist}
        keyExtractor={(e, i) => i.toString()}
        renderItem={renderItem}
      />
     //ここまで

    </SafeAreaView>
  );
};
```
#### ③ レンダリングする

```react
const Practice = () => {
  const [status, setStatus] = useState("All");
  const setStatusFilter = (status) => {
    setStatus(status);
  };

//レンダリング  ここから追記
  const renderItem = ({ item, index }) => {
    return (
      <View key={index} style={styles.itemContainer}>
        <View style={styles.itemLogo}>
          <Image
            style={styles.itemImage}
            source={{
              uri: "https://store.juventus.com/data/store/product/2/26919/product.jpg",
            }}
          />
        </View>

        <View style={styles.itemBody}>
          <Text style={styles.itemName}>{item.name}</Text>
        </View>

        <View
          style={[
            styles.itemStatus,
            {
              backgroundColor: item.status === "Purple" ? "#e5848e" : "#69C080",
            },
          ]}
        >
          <Text>{item.status}</Text>
        </View>
      </View>
    );
  };
//ここまで

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.listTab}>
        {listTab.map((e) => (
          <TouchableOpacity
            style={[styles.btnTab, status === e.status && styles.btnTabActive]}
            onPress={() => setStatusFilter(e.status)}
          >
            <Text
              style={[
                styles.textTab,
                status === e.status && styles.textTabActive,
              ]}
            >
              {e.status}
            </Text>
          </TouchableOpacity>
        ))}
      </View>

      <FlatList
        data={data}
        keyExtractor={(e, i) => i.toString()}
        renderItem={renderItem}
      />
    </SafeAreaView>
  );
};
```


CSS

```react
  itemContainer: {
    flexDirection: "row",
    paddingVertical: 15,
  },

  itemLogo: {
    padding: 10,
  },

  itemImage: {
    width: 50,
    height: 50,
  },
  itemBody: {
    flex: 1,
    paddingHorizontal: 10,
    justifyContent: "center",
  },
  itemName: {
    fontWeight: "bold",
    fontSize: 16,
  },
  itemStatus: {
    backgroundColor: "green",
    paddingHorizontal: 6,
    justifyContent: "center",
    right: 12,
  },
```
##Step5 ： ボタンをクリックした時に表示リストを変化させる（状態変化）
Purpleのボタンを押すと「Purple」のカテゴリが、Greenを押すと「Green」が表示されるようにします
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1675687/0bcbb5dd-5354-3fe3-3162-7fb4613c4e3c.png">
<img width="400" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1675687/f12d664a-3683-1751-d4fd-162cf55afad3.png">

#### ① useStateを設定
`const [status, setStatus] = useState("All");`の下に記述

```react
  const [datalist, setDatalist] = useState(data);
```

#### ② `setFunctionFilter`ファンクションの中身を作成

```react
const setStatusFilter = (status) => {
    //statusが"All"以外だったら（Purple and Greenなら）
    if (status !== "All") {
    //filterメソッドで、特定カテゴリのデータが表示される（何が起きているかわからんんん）
      setDatalist([...data.filter((e) => e.status === status)]);
    //"All"の場合は全リスト表示
    } else {
      setDatalist(data);
    }
    setStatus(status);
  };
```
`...data` の `...` は「スプレッド構文(Spread Syntax)」と言われ、配列やオブジェクトの要素を展開する構文です。
ここでは、Step4 で定義したリストデータ `data` の中身↓が展開されています。

```react
const data = [
  {
    name: "Ronaldo",
    status: "Green",
  },
  {
    name: "Messi",
    status: "Purple",
  },
  {
    name: "Kaka",
    status: "Green",
  },
  {
    name: "Mbappe",
    status: "Green",
  },
  {
    name: "Lukakau",
    status: "Purple",
  },
];
```


参考：[JSのスプレッド構文を理解する](https://qiita.com/akisx/items/682a4283c13fe336c547)


#### ③ `<FlatList />`の data の中身を datalist に書き換え

```react
      <FlatList
        data={datalist}  //ここを書き換え
        keyExtractor={(e, i) => i.toString()}
        renderItem={renderItem}
      />
```

#### ④ item separator を設定（オプション）
よくわからない

```react
  const separator = () => {
    return <View style={{ height: 1, backgroundColor: "#f1f1f1" }} />;
  };
```

```react
  <FlatList
   data={datalist}
   keyExtractor={(e, i) => i.toString()}
   renderItem={renderItem}
   ItemSeparatorComponent={separator} //ここを追記
  />
```

以上。
のちほど再編集していきます。