9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【最短実装】React+TS+Firestoreで簡単なCRUDアプリを実装する手順まとめ

Last updated at Posted at 2020-09-23

create-react-app + Firestore を使って、簡易的なCRUDアプリを実装する手順を解説します。極力最小構成にしているので、これから理解しようとしている方にはちょうど良いと思います!

ReactとFirestoreについてなんとなく理解している方が対象です。

ソースコード

コミット分けたので、ログの差分をご活用ください!

フロントの実装

React(TS)プロジェクトを作成

nodejsの環境は構築済みとします。

# npx create-react-app <プロジェクト名> [オプション]
npx create-react-app react-ts-firebase --typescript

開発環境で実行

cd <プロジェクト名>
npm run start

http://localhost:3000 へアクセス

スクリーンショット 2020-09-23 14.35.34.png

不要なCSSなどのファイルを削除

./
├── node_modules
│   └── ...
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.tsx
│   ├── index.tsx
│   ├── react-app-env.d.ts
│   ├── serviceWorker.ts
│   └── setupTests.ts
├── tsconfig.json
└── yarn.lock

一旦余計なソースを削除

index.tsx
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import * as serviceWorker from "./serviceWorker";

ReactDOM.render(<App />, document.getElementById("root"));

serviceWorker.unregister();
App.tsx
import React from "react";

function App() {
  return <div></div>;
}

export default App;

まずはダミーのデータを用いて描画処理

今回はAppコンポーネントに直書きします。

App.tsx
import React, { useState } from "react";

type User = {
  id: string;
  name: string;
  height: number;
};

function App() {
  const [users, setUsers] = useState<User[]>([
    { id: "ID1", name: "ゾンビ", height: 195 },
    { id: "ID2", name: "スケルトン", height: 199 },
    { id: "ID3", name: "クリーパー", height: 170 },
  ]);
  return (
    <div>
      <h2>Users</h2>
      {users.map((user) => (
        <div key={user.id}>
          {user.name} ({user.height}cm)
        </div>
      ))}
    </div>
  );
}

export default App;

Firestoreの登録

登録を進めたら、

プロジェクトを追加 -> プロジェクト名を入力 -> その他入力 -> 完了

サイドバーから「Cloud Firestore」を選択 -> データベース作成 -> テストモードをチェック -> Cloud Firestore のロケーション「asia-northeast3」を選択 -> 有効にする -> 完了

データベースが作成できたら、任意のデータを挿入

スクリーンショット 2020-09-23 14.17.58.png

コレクションはテーブルみたいなもので、ドキュメントはレコードみたいなものです。上記では、ドキュメントIDにランダム文字列を使用しています。

Firestoreとアプリケーションの連携

まず、Reactアプリケーションに以下のSDKを導入

npm install firebase

次に ./srcfirebase.ts を作成し、以下の内容を記述

firebase.ts
import firebase from "firebase/app";
import "firebase/firestore";
import { firebaseConfig } from "./firebaseConfig";

firebase.initializeApp(firebaseConfig);
export default firebase;
export const db = firebase.firestore();

firebaseConfig.ts の値は、webにてアプリを追加したのちに設定/全般ページの下の方で取得できます。

スクリーンショット 2020-09-23 14.50.24.png

スクリーンショット 2020-09-23 15.00.39.png

firebaseConfig.ts
export const firebaseConfig = {
  apiKey: "",
  authDomain: "",
  databaseURL: "",
  projectId: "",
  storageBucket: "",
  messagingSenderId: "",
  appId: "",
};

firebaseConfig.ts は認証情報が含まれるので、公開してはいけません。.gitignore に含めておきましょう。

.gitignore
...

# firebase
firebaseConfig.ts

一覧表示を実装

まず、連携が無事に出来ているかを確認

App.tsx
import React, { useState } from "react";

type User = {
  id: string;
  name: string;
  height: number;
};

function App() {
  const [users, setUsers] = useState<User[]>([]);

  // === 追記分 ===
  useEffect(() => {
    // 取得結果をコンソールに出力
    const usersRef = db.collection("users");
    usersRef.get().then((snapshot) => {
      snapshot.forEach((doc) => {
        console.log(doc.id, doc.data())
      });
    });
  }, []);
  // =============

  return (
    <div>
      <h2>Users</h2>
      {users.map((user) => (
        <div key={user.id}>
          {user.name} ({user.height}cm)
        </div>
      ))}
    </div>
  );
}

export default App;

ページを表示し、連携がうまくいっていたらデータがそのまま表示されるはずです。

スクリーンショット 2020-09-23 19.45.31.png

取得したデータを setUsers() を利用して state に代入

App.tsx
import React, { useEffect, useState } from "react";
import { db } from "./firebase";

type User = {
  id: string;
  name: string;
  height: number;
};

function App() {
  const [users, setUsers] = useState<User[]>([]);

  // === 追記分 ===
  const fetchUsersData = () => {
    const usersRef = db.collection("users");
    usersRef.get().then((snapshot) => {
      const newUsers: any[] = [];
      snapshot.forEach((doc) => {
        newUsers.push({
          id: doc.id,
          ...doc.data(),
        });
      });
      setUsers(newUsers);
    });
  };

  useEffect(() => {
    fetchUsersData();
  }, []);
  // =============

  return (
    <div>
      <h2>Users</h2>
      {users.map((user) => (
        <div key={user.id}>
          {user.name} ({user.height}cm)
        </div>
      ))}
    </div>
  );
}

export default App;

スクリーンショット 2020-09-23 20.04.32.png

追加、削除、更新の実装

削除処理

スクリーンショット 2020-09-23 20.28.59.png

削除したタイミングでデータを取得し直すように実装しました。それと、一応のエラー処理も実装してみました。

App.tsx
function App() {

  ...

  // 追加
  const handleDelete = (id: string) => {
    if (window.confirm("削除してもよろしいですか?")) {
      db.collection("users")
        .doc(id)
        .delete()
        .then(() => {
          fetchUsersData();
          alert("削除しました");
        })
        .catch(() => {
          alert("失敗しました");
        });
    }
  };

  ...

  return (
    <div>
      <h2>Users</h2>
      <table>
        <tbody>
          {users.map((user) => (
            <tr key={user.id}>
              <td>{user.name}</td>
              <td>{user.height}cm</td>
              <td>
                <button onClick={() => handleDelete(user.id)}>削除</button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

...

追加処理

スクリーンショット 2020-09-23 21.23.07.png

App.tsx
function App() {

  ...

  // 追加
  const [addUserName, setAddUserName] = useState<string>("");
  const [addUserHeight, setAddUserHeight] = useState<number>(200);

  ...

  // 追加
  const handleAdd = () => {
    if (window.confirm("追加してもよろしいですか?")) {
      db.collection("users")
        .add({
          name: addUserName,
          height: addUserHeight,
        })
        .then(() => {
          fetchUsersData();
          setAddUserHeight(200);
          setAddUserName("");
          alert("追加しました");
        })
        .catch(() => {
          alert("失敗しました");
        });
    }
  };

  ...

  return (
    <div>
      <h2>Users</h2>
      {/* === 追加分 === */}
      <div>
        <label>
          NAME:{" "}
          <input
            type="text"
            value={addUserName}
            onChange={(event) => setAddUserName(event.target.value)}
          />
        </label>
        <label>
          HEIGHT:{" "}
          <input
            type="number"
            value={addUserHeight}
            onChange={(event) => setAddUserHeight(event.target.valueAsNumber)}
          />
        </label>
        <button onClick={() => handleAdd()}>追加</button>
      </div>
      {/* ============= */}
      <table>
        <tbody>
          {users.map((user) => (
            <tr key={user.id}>
              <td>{user.name}</td>
              <td>{user.height}cm</td>
              <td>
                <button onClick={() => handleDelete(user.id)}>削除</button>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

...

更新処理

実装は省略しますが、set メソッドで実装できます。

db.collection('cities').doc('new-city-id').set(data);

最後に

短期間の開発ではこれでいいかもしれませんが、そうでない場合は共通化・モジュール化などしましょう!

リンク

9
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?