Next.js + reactfireでfirestoreを操作する

# 概要

Next.js で [reactfire](https://github.com/FirebaseExtended/reactfire)を使ってfirestoreを操作する方法

## 前提

- next 13.0.6
- react 18.2.0
- firebase 9.15.0
- reactfire 4.2.2

# 使い方

ポイントは、firebaseを利用するために、FirebaseAppProviderを使って、firebase appを生成するのと、FirebaseAppProviderで生成したfirebase appからfirestoreを生成するところ。authなどの生成も`FirebaseSDKProviders`で行えばいい。

## firestore SDKを提供するコンポーネントを作成する

このあたりが直観的でないのですが、FirebaseAppProviderでいろいろ用意しておいてほしいところですが、`FirestoreProvider`を使ってsdkにfirestoreのインスタンスを渡す必要があります。

```pages/firebase-provider.tsx
import {FirestoreProvider, useFirebaseApp} from 'reactfire';
import {getFirestore} from 'firebase/firestore';

export default function FirebaseSDKProviders({children}: any) {
    const app = useFirebaseApp();
    const firestore = getFirestore(app);
    return <FirestoreProvider sdk={firestore}>{children}</FirestoreProvider>;
}
```

## AppのComponentの親にFirebaseAppProvider等を追加する

`FirebaseAppProvider`と上で作成した`FirebaseSDKProviders`を`Component`の親に指定します
`firebaseConfig`はfirebaseの管理画面からウェブアプリを作ったときに出てくるスニペットを貼り付けてます

```pages/_app.tsx
import '../styles/globals.css'
import type {AppProps} from 'next/app'
import {FirebaseAppProvider} from "reactfire";
import FirebaseSDKProviders from "./firebase-provider";

// Your web app's Firebase configuration
const firebaseConfig = {
    apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    authDomain: "xxxxxxxxxx.firebaseapp.com",
    projectId: "xxxxxxxxxx",
    storageBucket: "xxxxxxxxxx.appspot.com",
    messagingSenderId: "999999999999",
    appId: "1:999999999999:web:xxxxxxxxxxxxx"
};

export default function App({Component, pageProps}: AppProps) {
    return <FirebaseAppProvider firebaseConfig={firebaseConfig}>
        <FirebaseSDKProviders>
            <Component {...pageProps} />
        </FirebaseSDKProviders>
    </FirebaseAppProvider>
}
```

## 利用する

長ったらしく書きましたが、大事なのは、`useFirestore`でfirestoreのインスタンスが利用できるところかと思います。あとは普通です。

```pages/todo/index.tsx
import React, {useState} from "react";
import {addDoc, collection,} from 'firebase/firestore';
import {useFirestore, useFirestoreCollectionData} from 'reactfire';

export default function Todo() {
    const [text, setText] = useState("");

    const firestore = useFirestore();
    const todo = collection(firestore, 'todo');
    const {status, data} = useFirestoreCollectionData(todo);

    const addTodo = async () => {
        await addDoc(todo, {todo: text})
        setText("")
    }

    if (status === 'loading') {
        return <p>Fetching todo list...</p>;
    }
    //console.log(data)
    return (
        <>
            <div>
                <input type="text" value={text} onChange={(event) => setText(event.target.value)}/>
                <button type="button" onClick={() => addTodo()}>add</button>
            </div>
            {data.map((d) => (
                <p key={d.NO_ID_FIELD}>{d.todo}</p>
            ))}
        </>
    );
};
```

# 付録

## プロジェクトの作成

```
% yarn create next-app
```

```
% firebase init
```

```
% yarn add firebase reactfire
```

## 参考にしたドキュメント、動画

- [Quick Intro To ReactJS ReactFire #2](https://www.youtube.com/watch?v=FoMbciaDMzY)
- [GitHub: quick-intro-reactfire-v4](https://github.com/aaronksaunders/quick-intro-reactfire-v4)
- [Error： SDK not found. useSdk must be called from within a provider · Discussion #499 · FirebaseExtended/reactfire · GitHub](https://github.com/FirebaseExtended/reactfire/discussions/499)