ReactとFirebaseで記事投稿アプリ作った感想 #2

ReactのuseStateフックを使って、投稿のタイトルや内容を管理する方法を基礎から解説しますね。具体的には、状態変数の宣言や、イベントハンドラーの使い方についてお話しします。

## 基礎知識
1. useStateフックとは？

ReactのuseStateフックは、コンポーネントの状態を管理するために使います。
状態を持たせることで、コンポーネントが再描画される際に、最新の状態を反映させることができます。
2. 状態変数の定義

状態変数は、タイトルや投稿内容などの情報を保存するために使います。
実装手順
1. useStateをインポートする
まず、ReactのコンポーネントファイルでuseStateをインポートします。

```
import React, { useState } from 'react';

```

2. 状態変数を定義する
次に、タイトルと投稿内容を格納するための状態変数をuseStateを使って定義します。
```
const CreatePost = () => {
    const [title, setTitle] = useState(''); // タイトル用の状態変数
    const [postText, setPostText] = useState(''); // 投稿内容用の状態変数
    // ここに他のコードが続きます...
};

```
useState('')の引数は、初期値です。ここでは空の文字列を指定しています。
3. 入力フォームを作成する
次に、タイトルと投稿内容を入力するためのフォームを作成します。<input>タグと<textarea>タグを使います。
```
return (
    <div>
        <input 
            type="text" 
            placeholder="タイトルを入力" 
            onChange={(e) => setTitle(e.target.value)} // タイトルが変わるたびに状態を更新
        />
        <textarea 
            placeholder="投稿内容を入力" 
            onChange={(e) => setPostText(e.target.value)} // 投稿内容が変わるたびに状態を更新
        />
        {/* 投稿ボタンなど他の要素もここに追加できます */}
    </div>
);

```

onChangeイベントハンドラーは、ユーザーが入力を変更したときに呼び出されます。
setTitle(e.target.value)は、入力された値をタイトルの状態に保存します。同様に、setPostText(e.target.value)は投稿内容の状態に保存します。

 #### まとめ
このように、useStateを使って状態を管理し、onChangeイベントを使って入力内容をリアルタイムで反映させることができます。これによって、ユーザーが入力したタイトルや投稿内容を後にクラウドファイアストアに保存するための準備が整います。

## Reactの基本
ステート（state）: コンポーネントの状態を管理します。ユーザーの入力に応じて変わります。
プロップス（props）: 親コンポーネントから子コンポーネントにデータを渡すための仕組み。

## useStateフック
```
import React, { useState } from 'react';

function CreatePost() {
  // タイトルと投稿内容を格納するための状態を宣言
  const [title, setTitle] = useState('');
  const [postText, setPostText] = useState('');

  return (
    <div>
      <input 
        type="text" 
        placeholder="タイトルを入力" 
        onChange={(e) => setTitle(e.target.value)} 
      />
      <textarea 
        placeholder="投稿内容を入力" 
        onChange={(e) => setPostText(e.target.value)} 
      />
      <button onClick={() => console.log(title, postText)}>投稿</button>
    </div>
  );
}

```

 ## 入力値の取得
ユーザーが入力した内容を取得するために、onChangeイベントを使います。このイベントは、入力フィールドが変更されるたびに発火します。

```
<input 
  type="text" 
  onChange={(e) => setTitle(e.target.value)} 
/>

```

ここでは、入力フィールドの値をsetTitle関数でtitleに保存しています。

## Firestoreへのデータ保存
Firestoreにデータを保存するためには、Firebaseの設定が必要です。まず、Firebaseをプロジェクトに追加しましょう。以下の手順を行います。

Firebaseプロジェクトを作成: Firebase Consoleで新しいプロジェクトを作成。
Firestoreを有効化: Firestoreデータベースを作成し、有効化します。
Firebase SDKのインストール: プロジェクトにFirebase SDKをインストールします。
```
npm install firebase

```

Firebaseを初期化: Firebaseの設定を行います。例えば、以下のように初期化できます。
```
import { initializeApp } from 'firebase/app';
import { getFirestore, collection, addDoc } from 'firebase/firestore';

// Firebaseの設定
const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_PROJECT_ID.appspot.com",
  messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
  appId: "YOUR_APP_ID"
};

// Firebaseを初期化
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

```

## データの追加
Firestoreにデータを追加するためには、addDocを使います。CreatePost関数の中で、ボタンがクリックされたときにFirestoreにデータを送る処理を追加します。
```
import { addDoc } from 'firebase/firestore';

async function createPost() {
  try {
    await addDoc(collection(db, 'posts'), {
      title: title,
      content: postText,
      createdAt: new Date(),
    });
    console.log("投稿が保存されました");
  } catch (error) {
    console.error("投稿の作成に失敗しました: ", error);
  }
}

```

## 完全なコード例
以下に、これまでの内容をまとめた完全なコード例を示します。
```
import React, { useState } from 'react';
import { initializeApp } from 'firebase/app';
import { getFirestore, collection, addDoc } from 'firebase/firestore';

// Firebaseの設定
const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_PROJECT_ID.appspot.com",
  messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
  appId: "YOUR_APP_ID"
};

// Firebaseを初期化
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

function CreatePost() {
  const [title, setTitle] = useState('');
  const [postText, setPostText] = useState('');

  const createPost = async () => {
    try {
      await addDoc(collection(db, 'posts'), {
        title: title,
        content: postText,
        createdAt: new Date(),
      });
      console.log("投稿が保存されました");
      setTitle(''); // 入力をクリア
      setPostText(''); // 入力をクリア
    } catch (error) {
      console.error("投稿の作成に失敗しました: ", error);
    }
  };

  return (
    <div>
      <input 
        type="text" 
        placeholder="タイトルを入力" 
        value={title}
        onChange={(e) => setTitle(e.target.value)} 
      />
      <textarea 
        placeholder="投稿内容を入力" 
        value={postText}
        onChange={(e) => setPostText(e.target.value)} 
      />
      <button onClick={createPost}>投稿</button>
    </div>
  );
}

export default CreatePost;

```

## エラーハンドリングと権限設定
Firestoreにデータを保存する際に、Insufficient permissionsというエラーが発生することがあります。これは、Firestoreのルールが原因です。以下のように、Firestoreのセキュリティルールを変更することで、書き込みを許可できます。
```
service cloud.firestore {
  match /databases/{database}/documents {
    match /posts/{post} {
      allow read, write: if request.auth != null; // 認証されているユーザーのみ許可
    }
  }
}

```

以上が、Reactを使ってユーザーからの入力を取得し、Firestoreにデータを保存する方法です。最初は難しいかもしれませんが、少しずつ慣れていくと思います。

1. データの取得
getDocs関数を使ってCloud Firestoreからデータを取得します。
useEffectフックを利用して、コンポーネントがマウントされたときにデータを1回だけ取得します。
取得したデータはuseStateで状態として保存し、表示します。
2. データの投稿
ユーザーがログインしているかどうかを確認し、ログイン時のみ投稿ボタンを表示します。
投稿する際は、setPostListを使用して、取得したデータを状態変数に格納します。
3. データの削除
各投稿に削除ボタンを付けて、handleDelete関数を作成します。
deleteDoc関数を使用して、特定のドキュメントを削除します。
4. 権限管理
ユーザーがログインしていない場合、投稿ページにアクセスしようとすると、リダイレクトされるように設定します。
5. エラーハンドリング
投稿や削除操作に関してエラーハンドリングを適切に行い、開発者ツールを使ってコンソールに出力することでデバッグを行います。
具体例コード
以下は、データの取得と投稿、削除に関する基本的なコードの一部です。
```
import React, { useEffect, useState } from 'react';
import { db } from './firebase'; // Firebaseの設定をインポート
import { getDocs, collection, deleteDoc, doc } from 'firebase/firestore';

const Blog = () => {
    const [postList, setPostList] = useState([]);

    useEffect(() => {
        const getPosts = async () => {
            const querySnapshot = await getDocs(collection(db, "posts"));
            const posts = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
            setPostList(posts);
        };
        getPosts();
    }, []);

    const handleDelete = async (id) => {
        await deleteDoc(doc(db, "posts", id));
        setPostList(postList.filter(post => post.id !== id));
    };

    return (
        <div>
            {postList.map(post => (
                <div key={post.id}>
                    <h2>{post.title}</h2>
                    <p>{post.text}</p>
                    <button onClick={() => handleDelete(post.id)}>削除</button>
                </div>
            ))}
        </div>
    );
};

export default Blog;

```
:::note info
これらの機能を組み合わせて、FirebaseとReactを使ったアプリケーションを構築することができます。ドキュメントを参考にしながら、必要な処理を実装していくことが重要です。
:::

# お疲れ様でした！