【firebase + React】firestoreから月間のデータと年間のデータに分けて取得したい人へ

## はじめに
Firestoreを使ってデータを取得する方法は多くありますが、特に月間と年間に分けて効率的にデータを取得する方法に困っている方も多いかと思います。この記事では、Datefnsを使って日付をフォーマットし、Firestoreから必要な期間のデータを簡単に取得する方法を紹介します。

## 必要な準備
* Firebaseプロジェクトの設定
Firebaseプロジェクトの作成し、firestoreのデータフィールドに以下の画像のようなdateフィールドを文字列で用意してください。

![スクリーンショット 2024-10-20 153857.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3809793/501a414f-1a34-d316-188f-158e10e71321.png)

* Datefnsの導入
インストール（バージョンは4.0.0-beta.1でした）
```
npm i date-fns
```

## 1.データを取得しstateで管理
データの取得の仕方はドキュメントを参考にしてください。（今回エラーハンドリングは省いています）
https://firebase.google.com/docs/firestore/query-data/get-data?hl=ja


```tsx
import { useMemo, useEffect, useState } from "react";
import { collection, getDocs } from "firebase/firestore";
import { db } from "../../firebase";
import { Transaction } from "../types"; //取得するデータの型

const app = () => {
  const [transactions, setTransactions] = useState<Transaction[]>([]);

  useEffect(() => {
    const fetchTransactions = async () => {
        const querySnapshot = await getDocs(collection(db, "Transactions"));

        const transactionsDate = querySnapshot.docs.map((doc) => {
          return {
            ...doc.data(),
          };
        });

        setTransactions(transactionsDate);
      },
      fetchTransactions();
  },[]);

  return <div></div>;
};

export default app;

```

## 2.フォーマット作成
Datefnsの`format`関数にはフォーマット文字列を渡して、日付の各部分を表示します。いくつかのプレースホルダーを紹介します。
* `yyyy` - 西暦年（4桁）
* `yy` - 西暦年（2桁）
* `MM` - 月（2桁、例："10"）
* `dd` - 日（2桁、例："21"）

```ts
import { format } from "date-fns";

//月のフォーマット
export function formatMonth(date: Date): string {
  return format(date, "yyyy-MM");
}

//年のフォーマット
export function formatYear(date: Date): string {
  return format(date, "yyyy");
}
```

## 3.月間データ、年間データ取得
* それぞれ `useMemo` フックを使って計算を効率化し、依存する `transactions` に変更がある場合のみ再計算されます。
* `startsWith` メソッドは、文字列が特定の文字列で始まるかどうかをチェックするためのメソッドです。例えば、"2024-10", "2024" などの文字列形式で日付を扱う場合に、`startsWith` を使ってフィルタリングを行います。

```tsx
import { collection, getDocs } from "firebase/firestore";
import { db } from "../../firebase";
import { Transaction } from "../types"; //取得するデータの型
import { formatMonth, formatYear } from "../utils/formatting";
import { useMemo, useEffect, useState } from "react";

const app = () => {
  const [transactions, setTransactions] = useState<Transaction[]>([]);

  useEffect(() => {
    const fetchTransactions = async () => {
        const querySnapshot = await getDocs(collection(db, "Transactions"));

        const transactionsDate = querySnapshot.docs.map((doc) => {
          return {
            ...doc.data(),
          };
        });

        setTransactions(transactionsDate);
      },
      fetchTransactions();
  },[]);

  //今月のデータを取得
  const monthlyTransactions = useMemo(() => {
    return transactions.filter((transaction) =>
      transaction.date.startsWith(formatMonth(new Date()))
    );
  }, [transactions]);

  //今年のデータをすべて取得(ここでエラーが出たらformatYearの引数にnew DateをStateで新しく定義して入れて)
  const yearTransactions = useMemo(() => {
    return transactions.filter((transaction) =>
      transaction.date.startsWith(formatYear(new Date()))
    );
  }, [transactions]);

  return <div></div>;
};

export default app;

```

## おわり
FirestoreとReactを組み合わせて、月間と年間のデータを効率よく取得する方法について説明しました。Firestoreからデータを取得し、`useMemo`を使って今月と今年のトランザクションをフィルタリングすることで、必要なデータを素早く表示できます。この方法を使えば、パフォーマンスを向上させながら、ユーザーが求める特定の期間のデータを柔軟に提供できると思います。

Firestoreでのデータ取得はシンプルなクエリとReactのフックを組み合わせることで非常にパワフルになります。ぜひ、今回紹介した方法を参考に、あなたのアプリケーションでも効果的にデータを管理してみてください。