ReactとFirebaseで作る簡単なTo-Doアプリ構築ガイド

ReactとFirebaseは、モダンなWebアプリケーション開発において非常に人気の高い技術スタックです。本記事では、この2つを組み合わせて、初心者でも簡単に構築できるTo-Doアプリの作り方を解説します。このプロジェクトを通じて、Reactを使ったUIの構築方法や、Firebaseを活用したリアルタイムデータベースの操作方法を学ぶことができます。完成したアプリを通じて、基本的なCRUD操作(作成、読み取り、更新、削除)の実装を体験できるでしょう。さあ、一歩ずつ進んでいきましょう!

目次

開発環境のセットアップ


ReactとFirebaseを使用するには、まず開発環境を準備する必要があります。以下では、必要なツールと手順を解説します。

1. 必要なツールのインストール


ReactとFirebaseを使った開発を始めるために、以下のツールをインストールします:

Node.jsとnpm


Node.jsはJavaScriptのランタイム環境で、npm(Node Package Manager)は依存関係を管理するためのツールです。

  1. Node.js公式サイトにアクセスしてLTSバージョンをインストールしてください。
  2. インストール後、以下のコマンドでバージョンを確認します:
   node -v
   npm -v

コードエディタ


人気のあるコードエディタとしてVisual Studio Codeを使用します。インストールしておきましょう。

2. Reactプロジェクトのセットアップ


以下のコマンドでReactアプリを作成します:

npx create-react-app todo-app
cd todo-app

3. Firebase CLIのインストール


Firebase CLIをインストールすることで、Firebaseプロジェクトのセットアップと管理が容易になります。以下のコマンドを実行してください:

npm install -g firebase-tools

インストール後、Firebase CLIが正しく動作するかを確認します:

firebase --version

4. Firebaseプロジェクトへのログイン


Firebase CLIを使用してGoogleアカウントでログインします:

firebase login

5. 準備完了


これで、ReactとFirebaseを使用するための基本的な環境が整いました。次に、ReactアプリとFirebaseプロジェクトの初期設定を進めていきます。

プロジェクトの初期設定


ReactアプリケーションをFirebaseプロジェクトと連携するための初期設定を行います。以下の手順に従ってセットアップを進めましょう。

1. Reactプロジェクトの確認


前のステップで作成したReactプロジェクトディレクトリに移動し、開発サーバーを起動して初期状態を確認します:

npm start

ブラウザにアプリが表示されたら、セットアップが正しく進んでいることを確認できます。

2. Firebaseプロジェクトの作成


Firebaseコンソールにアクセスして、新しいプロジェクトを作成します:

  1. Firebaseコンソールにログインします。
  2. プロジェクトを作成をクリックし、プロジェクト名を入力して続行します。
  3. Googleアナリティクスの設定は任意で構いません。選択後、プロジェクトを作成します。

3. Firebaseプロジェクトの設定


作成したプロジェクトをReactアプリで使用できるように設定します:

  1. プロジェクトダッシュボードで「Webアプリを追加」ボタンをクリックします。
  2. Webアプリの名前を入力し、「アプリを登録」を選択します。
  3. Firebase SDK設定スニペットが表示されるので、後で使用するためにメモしておきます。

4. Firebase SDKのインストール


Reactプロジェクト内でFirebaseを使用するために、以下のコマンドを実行してSDKをインストールします:

npm install firebase

5. Firebaseの初期化


Reactプロジェクトのsrcディレクトリに新しいファイルfirebaseConfig.jsを作成し、Firebaseの設定スニペットをコピーします:

// src/firebaseConfig.js
import { initializeApp } from "firebase/app";

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
  appId: "YOUR_APP_ID",
};

const app = initializeApp(firebaseConfig);
export default app;

6. Firebaseプロジェクトの確認


ReactプロジェクトがFirebaseと接続できるか確認します。今後、この設定をもとにFirestoreやAuthenticationの機能を使用します。

初期設定が完了したら、次のステップとしてFirebaseデータベースの構築を進めます。

Firebaseデータベースの設定


To-Doアプリでは、タスクのデータを管理するためにFirebaseのCloud Firestoreを使用します。このセクションでは、Firestoreデータベースの設定方法と基本的な構造を説明します。

1. Firestoreデータベースの有効化

  1. Firebaseコンソールでプロジェクトを開きます。
  2. 左側のメニューから「Firestore Database」を選択します。
  3. 「データベースを作成」をクリックし、データベースの作成手順を進めます。
  • データベースモード:開始モードは「テストモード」を選択します(後で適切なセキュリティルールに変更します)。
  • データベースの場所を選択し、作成を完了します。

2. Firestoreの基本構造


Firestoreは「コレクション」と「ドキュメント」という階層構造でデータを管理します。

  • コレクション:同じ種類のデータをまとめたグループ(例:tasks)。
  • ドキュメント:コレクション内の個別のデータ(例:task1task2)。

今回のTo-Doアプリでは以下のようなデータ構造を使用します:

  • コレクション名:tasks
  • ドキュメントフィールド例:
    • id: タスクID
    • title: タスクのタイトル
    • completed: 完了状態(true または false

3. Firestoreルールの設定


「テストモード」でデータベースを作成した場合、デフォルトでは全てのユーザーが読み書きできます。セキュリティ向上のため、以下のようにルールを設定します:

  1. Firebaseコンソールの「Firestore Database」セクションで「ルール」を開きます。
  2. ルールを以下のように変更します:
   rules_version = '2';
   service cloud.firestore {
     match /databases/{database}/documents {
       match /tasks/{taskId} {
         allow read, write: if request.auth != null;
       }
     }
   }

これにより、認証済みユーザーのみデータの読み書きが可能になります。

4. 初期データの追加


FirebaseコンソールのFirestoreデータベースに初期データを追加します:

  1. 「コレクションを開始」をクリックし、コレクション名にtasksを入力します。
  2. ドキュメントIDを自動生成し、以下のフィールドを追加します:
  • title: サンプルタスク(例:「最初のタスク」)
  • completed: false

5. Firestoreとの接続テスト


後のセクションでReactからFirestoreにデータを読み書きするコードを実装します。これでデータベースの準備は整いました。

次のステップでは、To-Doアプリの主要部分であるReactコンポーネントの作成に進みます。

Reactコンポーネントの作成


To-Doアプリの動作を支える主要なReactコンポーネントを作成します。このセクションでは、アプリの基本的なUIを構築するためのコンポーネントを実装します。

1. コンポーネント構成の計画


アプリのUIを効率的に構築するために、以下の主要コンポーネントを作成します:

  1. App: アプリ全体の構造を管理するコンテナコンポーネント。
  2. TodoList: タスク一覧を表示するコンポーネント。
  3. TodoItem: 個々のタスクを表すコンポーネント。
  4. TodoForm: 新しいタスクを追加するためのフォームコンポーネント。

2. コンポーネントの作成

Appコンポーネント


以下のコードをApp.jsに記述します:

import React, { useState } from "react";
import TodoList from "./components/TodoList";
import TodoForm from "./components/TodoForm";

function App() {
  const [tasks, setTasks] = useState([]);

  const addTask = (title) => {
    const newTask = { id: Date.now(), title, completed: false };
    setTasks([...tasks, newTask]);
  };

  const toggleTaskCompletion = (id) => {
    setTasks(
      tasks.map((task) =>
        task.id === id ? { ...task, completed: !task.completed } : task
      )
    );
  };

  return (
    <div>
      <h1>To-Do App</h1>
      <TodoForm addTask={addTask} />
      <TodoList tasks={tasks} toggleTaskCompletion={toggleTaskCompletion} />
    </div>
  );
}

export default App;

TodoListコンポーネント


以下のコードをcomponents/TodoList.jsに記述します:

import React from "react";
import TodoItem from "./TodoItem";

function TodoList({ tasks, toggleTaskCompletion }) {
  return (
    <ul>
      {tasks.map((task) => (
        <TodoItem
          key={task.id}
          task={task}
          toggleTaskCompletion={toggleTaskCompletion}
        />
      ))}
    </ul>
  );
}

export default TodoList;

TodoItemコンポーネント


以下のコードをcomponents/TodoItem.jsに記述します:

import React from "react";

function TodoItem({ task, toggleTaskCompletion }) {
  return (
    <li>
      <span
        style={{
          textDecoration: task.completed ? "line-through" : "none",
        }}
      >
        {task.title}
      </span>
      <button onClick={() => toggleTaskCompletion(task.id)}>
        {task.completed ? "Undo" : "Complete"}
      </button>
    </li>
  );
}

export default TodoItem;

TodoFormコンポーネント


以下のコードをcomponents/TodoForm.jsに記述します:

import React, { useState } from "react";

function TodoForm({ addTask }) {
  const [input, setInput] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();
    if (input.trim() !== "") {
      addTask(input);
      setInput("");
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder="Add a new task"
      />
      <button type="submit">Add</button>
    </form>
  );
}

export default TodoForm;

3. コンポーネントの連携


App.jsで各コンポーネントを統合し、タスクの追加、表示、完了状態の切り替えができる基本的なUIが完成します。

4. 次のステップ


次に、Firebaseとデータを連携させて、タスクデータを永続化します。これにより、リロードしてもデータが失われない仕組みを構築します。

Firebaseとのデータ連携


To-DoアプリのデータをFirebase Firestoreに保存し、リアルタイムでデータを取得できるようにします。このセクションでは、Firestoreを使用したデータの保存・取得・更新の実装方法を解説します。

1. Firestoreのセットアップ


Firestoreとの接続を実現するため、前のステップで作成したfirebaseConfig.jsを利用します。このファイルをインポートしてFirestore機能を利用できるようにします。

2. Firestoreへの依存を追加


firebase/firestoreモジュールをインストールしてFirestoreの操作をサポートします:

npm install firebase

3. Firebase Firestoreとの連携

Firestoreのインポートと初期化


firebaseConfig.jsを利用してFirestoreを初期化します。以下のコードをApp.jsに記述します:

import { getFirestore, collection, addDoc, onSnapshot, doc, updateDoc } from "firebase/firestore";
import firebaseApp from "./firebaseConfig";

const db = getFirestore(firebaseApp);
const tasksCollection = collection(db, "tasks");

Firestoreからデータを取得


Firestoreに保存されたタスクデータをリアルタイムで取得するため、onSnapshotを使用します:

useEffect(() => {
  const unsubscribe = onSnapshot(tasksCollection, (snapshot) => {
    const tasksData = snapshot.docs.map((doc) => ({
      id: doc.id,
      ...doc.data(),
    }));
    setTasks(tasksData);
  });

  return () => unsubscribe();
}, []);

Firestoreにデータを追加


新しいタスクをFirestoreに保存します:

const addTask = async (title) => {
  await addDoc(tasksCollection, {
    title,
    completed: false,
  });
};

Firestoreのデータを更新


タスクの完了状態を切り替えるには、updateDocを使用します:

const toggleTaskCompletion = async (id, completed) => {
  const taskDoc = doc(db, "tasks", id);
  await updateDoc(taskDoc, {
    completed: !completed,
  });
};

4. アプリ全体でFirestore機能を統合

以下はApp.jsの全体コードです:

import React, { useState, useEffect } from "react";
import { getFirestore, collection, addDoc, onSnapshot, doc, updateDoc } from "firebase/firestore";
import firebaseApp from "./firebaseConfig";
import TodoList from "./components/TodoList";
import TodoForm from "./components/TodoForm";

const db = getFirestore(firebaseApp);
const tasksCollection = collection(db, "tasks");

function App() {
  const [tasks, setTasks] = useState([]);

  useEffect(() => {
    const unsubscribe = onSnapshot(tasksCollection, (snapshot) => {
      const tasksData = snapshot.docs.map((doc) => ({
        id: doc.id,
        ...doc.data(),
      }));
      setTasks(tasksData);
    });

    return () => unsubscribe();
  }, []);

  const addTask = async (title) => {
    await addDoc(tasksCollection, {
      title,
      completed: false,
    });
  };

  const toggleTaskCompletion = async (id, completed) => {
    const taskDoc = doc(db, "tasks", id);
    await updateDoc(taskDoc, {
      completed: !completed,
    });
  };

  return (
    <div>
      <h1>To-Do App</h1>
      <TodoForm addTask={addTask} />
      <TodoList tasks={tasks} toggleTaskCompletion={toggleTaskCompletion} />
    </div>
  );
}

export default App;

5. Firebase連携の動作確認


アプリを起動して、新しいタスクを追加したり、完了状態を切り替える機能をテストします。Firestoreデータベースに正しくデータが保存され、リアルタイムで反映されることを確認します。

6. 次のステップ


Firebaseとの連携が完了したので、次にアプリのデザインとスタイリングを進めます。これにより、使いやすく視覚的に魅力的なアプリを作成できます。

アプリのスタイリング


To-Doアプリの基本的なデザインとレイアウトを整え、使いやすく魅力的なインターフェースを作成します。このセクションでは、CSSを使用してアプリにスタイリングを適用する方法を解説します。

1. CSSファイルの作成と準備


スタイリングを管理するために、以下の手順でCSSファイルを作成します:

  1. srcフォルダ内にApp.cssを作成します。
  2. アプリの全体的なスタイルを記述します。

2. グローバルスタイルの適用


以下のCSSをApp.cssに記述します:

body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f4f4f9;
}

h1 {
  text-align: center;
  color: #333;
}

form {
  display: flex;
  justify-content: center;
  margin: 20px 0;
}

input[type="text"] {
  width: 300px;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 5px;
  margin-right: 10px;
}

button {
  padding: 10px 20px;
  border: none;
  background-color: #28a745;
  color: white;
  border-radius: 5px;
  cursor: pointer;
}

button:hover {
  background-color: #218838;
}

3. TodoListとTodoItemのスタイル


各タスクのリストを見やすく整えます:

ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
}

li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-color: white;
  padding: 10px 20px;
  margin: 10px auto;
  width: 80%;
  border-radius: 5px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

li span {
  flex-grow: 1;
  margin-right: 10px;
}

li button {
  padding: 5px 10px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

li button:hover {
  background-color: #0056b3;
}

4. App.jsへのCSS適用


App.jsでCSSを適用するため、以下のようにApp.cssをインポートします:

import './App.css';

5. UIの調整と確認


アプリを起動してスタイルが適用されているか確認します。フォーム、ボタン、リストアイテムが整然と表示されていることを確認してください。

6. レスポンシブデザインの追加(任意)


モバイルデバイスでも快適に利用できるようにするため、以下のようにメディアクエリを追加します:

@media (max-width: 600px) {
  input[type="text"] {
    width: 80%;
  }

  li {
    width: 90%;
  }
}

7. 次のステップ


アプリのスタイリングが完成したので、次にアプリ全体の動作を確認し、エラー解消を行う段階に進みます。スタイリングによってユーザーの操作性が向上しているかもチェックしましょう。

完成したアプリの動作確認


アプリが正しく動作していることを確認し、必要に応じてエラーやバグを修正します。このセクションでは、To-Doアプリ全体のテスト方法と問題解決の手順を解説します。

1. アプリの基本機能をテスト


以下の機能が正しく動作することを確認します:

1.1 新しいタスクの追加

  • タスクタイトルを入力して「Add」ボタンをクリックします。
  • 入力されたタスクがリストに追加されることを確認します。
  • Firebase Firestoreにデータが保存されているか、コンソールで確認します。

1.2 タスクの完了状態の切り替え

  • タスクの「Complete」ボタンをクリックします。
  • 該当のタスクが取り消し線付きで表示され、Firestore内のデータが更新されることを確認します。

1.3 リアルタイム同期

  • 別のブラウザウィンドウでアプリを開きます。
  • 一方のウィンドウでタスクを追加・更新し、他方で変更がリアルタイムに反映されることを確認します。

2. エラーの確認と修正

2.1 データがFirestoreに保存されない

  • 原因:Firestoreのセキュリティルールが正しく設定されていない可能性があります。
  • 解決方法:Firebaseコンソールの「ルール」タブで、認証済みユーザーのみアクセスできるよう設定されているか確認します。

2.2 タスクのリストが表示されない

  • 原因:Firestoreからのデータ取得に問題がある可能性があります。
  • 解決方法:コンソールでエラーメッセージを確認し、Firestoreの設定やインターネット接続を見直します。

2.3 UIが崩れる

  • 原因:CSSの指定が適切でない可能性があります。
  • 解決方法:ブラウザの開発者ツールを使用してスタイルの問題箇所を特定し修正します。

3. アプリのパフォーマンス最適化(任意)

  • 不要なリレンダリングを防ぐ:ReactのuseCallbackReact.memoを使用して、パフォーマンスを向上させます。
  • データのキャッシュ:Firestoreからのデータ取得時にキャッシュを利用して読み込み速度を改善します。

4. チームメンバーやユーザーからのフィードバック

  • 他の開発者やユーザーにアプリを試用してもらい、操作感やバグについて意見をもらいます。
  • そのフィードバックを基に改良を行います。

5. 最終チェック


すべての機能を確認したら、完成したアプリケーションをデプロイする準備を進めます。動作確認の段階で見つかった課題を解決し、アプリがスムーズに動作することを確認しましょう。

6. 次のステップ


動作確認とエラー修正が完了したら、応用機能の追加を検討します。タスクの優先度や締切の設定機能を追加することで、アプリの実用性をさらに高めることができます。

応用機能の追加


完成したTo-Doアプリにさらなる実用性を加えるため、タスクの優先度や締切日を設定する機能を追加します。このセクションでは、これらの機能の実装方法を解説します。

1. タスクの優先度機能

1.1 データ構造の変更


Firestoreでタスクのデータ構造にpriorityフィールドを追加します:

const addTask = async (title, priority) => {
  await addDoc(tasksCollection, {
    title,
    completed: false,
    priority, // 優先度を保存
  });
};

1.2 優先度選択UIの追加


TodoFormコンポーネントにドロップダウンを追加して、ユーザーが優先度を選択できるようにします:

function TodoForm({ addTask }) {
  const [input, setInput] = useState("");
  const [priority, setPriority] = useState("Low");

  const handleSubmit = (e) => {
    e.preventDefault();
    if (input.trim() !== "") {
      addTask(input, priority);
      setInput("");
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder="Add a new task"
      />
      <select value={priority} onChange={(e) => setPriority(e.target.value)}>
        <option value="Low">Low</option>
        <option value="Medium">Medium</option>
        <option value="High">High</option>
      </select>
      <button type="submit">Add</button>
    </form>
  );
}

1.3 優先度による表示スタイルの変更


TodoItemでタスクの優先度に応じたスタイリングを追加します:

function TodoItem({ task, toggleTaskCompletion }) {
  const getPriorityColor = (priority) => {
    switch (priority) {
      case "High":
        return "red";
      case "Medium":
        return "orange";
      default:
        return "green";
    }
  };

  return (
    <li style={{ borderLeft: `5px solid ${getPriorityColor(task.priority)}` }}>
      <span
        style={{
          textDecoration: task.completed ? "line-through" : "none",
        }}
      >
        {task.title}
      </span>
      <button onClick={() => toggleTaskCompletion(task.id, task.completed)}>
        {task.completed ? "Undo" : "Complete"}
      </button>
    </li>
  );
}

2. 締切日の設定機能

2.1 データ構造の変更


FirestoreのデータにdueDateフィールドを追加します:

const addTask = async (title, priority, dueDate) => {
  await addDoc(tasksCollection, {
    title,
    completed: false,
    priority,
    dueDate, // 締切日を保存
  });
};

2.2 締切日入力フィールドの追加


TodoFormに締切日を入力するフィールドを追加します:

const [dueDate, setDueDate] = useState("");

<form onSubmit={handleSubmit}>
  <input
    type="text"
    value={input}
    onChange={(e) => setInput(e.target.value)}
    placeholder="Add a new task"
  />
  <select value={priority} onChange={(e) => setPriority(e.target.value)}>
    <option value="Low">Low</option>
    <option value="Medium">Medium</option>
    <option value="High">High</option>
  </select>
  <input
    type="date"
    value={dueDate}
    onChange={(e) => setDueDate(e.target.value)}
  />
  <button type="submit">Add</button>
</form>

2.3 締切日の表示


TodoItemコンポーネントでタスクの締切日を表示します:

<span>{task.dueDate ? `Due: ${task.dueDate}` : "No deadline"}</span>

3. 応用機能の動作確認

  1. タスクを追加し、優先度や締切日が正しく保存され、表示されるか確認します。
  2. Firebaseコンソールで保存されたデータを確認します。

4. 次のステップ


応用機能を追加したことで、アプリの実用性が大幅に向上しました。次に、アプリをデプロイしてユーザーに公開する準備を進めます。

まとめ


本記事では、ReactとFirebaseを組み合わせた簡単なTo-Doアプリの構築方法を解説しました。アプリの基本機能の実装から始まり、Firebase Firestoreを活用したデータの保存・取得、さらにタスクの優先度や締切日を設定する応用機能の追加までを網羅しました。

これにより、以下の重要なスキルを習得できました:

  • Reactを使ったUI構築と状態管理
  • Firebase Firestoreを利用したリアルタイムデータの管理
  • 実用的な機能の設計と実装

次のステップとして、アプリをデプロイして他のユーザーと共有する方法や、さらに高度な認証機能や通知機能の追加を検討してみてください。このプロジェクトを通じて得た知識を活かし、より多機能で実用的なアプリケーションを開発していきましょう。

コメント

コメントする

目次