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
へアクセス
不要な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
一旦余計なソースを削除
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();
import React from "react";
function App() {
return <div></div>;
}
export default App;
まずはダミーのデータを用いて描画処理
今回はAppコンポーネントに直書きします。
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」を選択 -> 有効にする -> 完了
データベースが作成できたら、任意のデータを挿入
コレクションはテーブルみたいなもので、ドキュメントはレコードみたいなものです。上記では、ドキュメントIDにランダム文字列を使用しています。
Firestoreとアプリケーションの連携
まず、Reactアプリケーションに以下のSDKを導入
npm install firebase
次に ./src
に 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にてアプリを追加したのちに設定/全般ページの下の方で取得できます。
export const firebaseConfig = {
apiKey: "",
authDomain: "",
databaseURL: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
};
firebaseConfig.ts
は認証情報が含まれるので、公開してはいけません。.gitignore
に含めておきましょう。
...
# firebase
firebaseConfig.ts
一覧表示を実装
まず、連携が無事に出来ているかを確認
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;
ページを表示し、連携がうまくいっていたらデータがそのまま表示されるはずです。
取得したデータを setUsers()
を利用して state に代入
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;
追加、削除、更新の実装
削除処理
削除したタイミングでデータを取得し直すように実装しました。それと、一応のエラー処理も実装してみました。
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>
);
}
...
追加処理
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);
最後に
短期間の開発ではこれでいいかもしれませんが、そうでない場合は共通化・モジュール化などしましょう!