5
4

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 1 year has passed since last update.

【個人開発の記録】React.js+TypeScript+FirebaseでCRUDアプリ開発

Last updated at Posted at 2022-02-26

背景

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
※この記事は現在執筆中です
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

ついに個人開発を始めることにした。
今回開発するWebアプリはCRUDアプリ。
Webページ上で動作し、ページを閉じてもデータを保持しておくことができる簡単なもの。

その開発の全手順をこの記事にまとめておくことにする。

開発するアプリについて

  • CRUDアプリ
  • Webページ上で動作する
  • ページを閉じてもデータは保持しておくことができる

開発言語/フレームワーク

  • フロントエンド:React.js + TypeScript
  • バックエンド:Firebase

開発手順

React+TypeScriptアプリの雛形を作成する

  • ターミナルにて下記コマンドを実行して、React+TypeScriptアプリの雛形を作成する
npx create-react-app kashikojin1 --template typescript
  • ターミナルにて、上記で作成したkashikojin1配下のpackage.josnのあるディレクトリで、下記コマンドを実行して、FirebaseのSDKとルーティング用のパッケージをインストールする
npm install firebase react-router-dom @types/react-router-dom
  • React + TypeScriptのアプリを立ち上げてみる

ターミナルにて、package.josnのあるディレクトリで、下記コマンドを実行するとChromeでアプリが起動する

npm run start

スクリーンショット 2022-02-25 19.48.42.png

Firebaseのプロジェクトを作成する

コレクションとドキュメントはこんな感じ
(これはデータベースでいうテーブルとデータ)

スクリーンショット 2022-02-26 10.33.00.png

スクリーンショット 2022-02-26 10.33.04.png

React+TypeScript+Firebaseをいろいろ触ってみた(やらなくていい)

1.データ(コレクションの中身の全ドキュメント)を表示する

先程作成したデータ(ドキュメント)を画面に表示してみる

プロジェクトのフォルダ構成を下記のようにする
★マークのファイルはこれから新規作成するファイル
☆マークのファイルはこれから編集するファイル

kashikojin1(プロジェクト名)
  └┬ build
   ┝ node_modules
   ┝ public
   ┝ src
   |   └┬ App.css
   |    ┝ App.tsx ☆
   |    ┝ firebase.tsx ★
   |    ┝ index.css
   |    ┝ index.js
   |    └ api
   |        └ hello.js
   ┝ .gitignore
   ┝ package-lock.json
   ┝ package.json
   ┝ README.md
   ┝ tsconfig.json
   └ .env ★

では★で示したファイルを作成していこう

.env(新規作成)
REACT_APP_FIREBASE_KEY="Firebaseで作成したウェブアプリのapiKey"
REACT_APP_FIREBASE_DOMAIN="Firebaseで作成したウェブアプリのauthDomain"
REACT_APP_FIREBASE_PROJECT_ID="Firebaseで作成したウェブアプリのprojectId"
REACT_APP_FIREBASE_STORAGE_BUCKET="Firebaseで作成したウェブアプリのstorageBucket"
REACT_APP_FIREBASE_SENDER_ID="Firebaseで作成したウェブアプリのmessagingSenderId"
REACT_APP_FIREBASE_APP_ID="Firebaseで作成したウェブアプリのappId"
REACT_APP_FIREBASE_MEASUREMENT_ID="Firebaseで作成したウェブアプリのmeasurementId"
kashikojin1/src/firebase.tsx(新規作成)
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
import { getStorage } from 'firebase/storage';

const firebaseConfig = {
  apiKey: process.env.REACT_APP_FIREBASE_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_APP_ID,
  measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID,
};

const app = initializeApp(firebaseConfig)

export const db = getFirestore();
export const storage = getStorage();

export default app;
kashikojin1/src/App.tsx(編集)
import { db } from '../src/firebase';
import { useState, useEffect }  from 'react';
import { collection, getDocs, QuerySnapshot } from 'firebase/firestore';
import './App.css';

type User = {
  name: string;
  email: string;
  age: number;
  admin: boolean;
};

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

  useEffect(() => {
    const usersCollectionRef = collection(db, 'users');
    getDocs(usersCollectionRef).then((querySnapshot) => {
      const  userList: User[] = [];
      let count: number = 0;
      querySnapshot.docs.map((doc, index) => {
        if (count === index ) {
          const user: User= {
            name: doc.data().name,
            email: doc.data().email,
            age: doc.data().age,
            admin: doc.data().admin,
          };
          userList.push(user);
          count += 1;
        };
      });
      setUsers(userList);
    });
  }, []);

  return (
    <div>
      {users.map((user, index) => (
        <div key={index.toString()}>{user.name}</div>
      ))}
    </div>
  );
}

export default App;

ターミナルにて、package.josnのあるディレクトリで、下記コマンドを実行するとChromeでアプリが起動する

npm run start

画面にコレクション1件(ドキュメント2件分)の文字列が出力することを確認できる

2.データ(指定したドキュメント)を表示する

App.tsxの中身を少し変えてみる

kashikojin1/src/App.tsx(編集)
import { db } from '../src/firebase';
import { useState, useEffect }  from 'react';
import { doc, getDoc } from 'firebase/firestore';
import './App.css';

type User = {
  name: string;
  email: string;
  age: number;
  admin: boolean;
};

function App() {
  useEffect(() => {
    const usersCollectionRef = doc(db, 'users', 'lNQjamODq8TnRoJcvBFC');
    getDoc(usersCollectionRef).then((documentSnapshot) => {
      if (documentSnapshot.exists()) {
        console.log(documentSnapshot.data());
      } else {
        console.log('No such document!');
      }
    });
  }, []);

  return (
    <div>
    </div>
  );
}

export default App;

ターミナルにて、package.josnのあるディレクトリで、下記コマンドを実行するとChromeでアプリが起動する

npm run start

コンソールにドキュメント1件分の内容が出力することを確認できる

3.Cloud Firestoreにてデータ(ドキュメント)を追加したときリアルタイムでデータを出力する

App.tsxの中身を少し変えてみる

kashikojin1/src/App.tsx(編集)
import { db } from '../src/firebase';
import { useEffect }  from 'react';
import { collection, onSnapshot } from 'firebase/firestore';
import './App.css';

type User = {
  name: string;
  email: string;
  age: number;
  admin: boolean;
};

function App() {

  useEffect(() => {
    const usersCollectionRef = collection(db, 'users');
    const unsub = onSnapshot(usersCollectionRef, (querySnapshot) => {
      querySnapshot.docs.map((doc) => {
        console.log(doc.data());
      });
    });
    return unsub;
  }, []);

  return (
    <div>
    </div>
  );
}

export default App;

ターミナルにて、package.josnのあるディレクトリで、下記コマンドを実行するとChromeでアプリが起動する

npm run start

Cloud Firestoreにてデータ(ドキュメント)を追加すると、ドキュメントがコンソールに出力することを確認できる

4.React+TypeScriptアプリの画面上からデータを追加する

App.tsxの中身を少し変えてみる

kashikojin1/src/App.tsx(編集)
import { useState,useEffect }  from 'react';
import { db } from '../src/firebase';
import { addDoc, collection, onSnapshot } from 'firebase/firestore';
import { useForm, SubmitHandler } from 'react-hook-form';
import './App.css';

type User = {
  name: string;
  email: string;
  age: number;
  admin: boolean;
};

function App() {
  const [users, setUsers] = useState<User[]>([]);
  const { register,
          handleSubmit,
          watch,
          formState: { errors }
        } = useForm<User>();

  const onSubmit: SubmitHandler<User> = (data) => {
    console.log('onSubmit', data);
    const usersCollectionRef = collection(db, 'users');
    const documentRef = addDoc(usersCollectionRef, {
      name: data.name,
      email: data.email,
      age: data.age,
      admin: false,
    });
    console.log(documentRef);
  };
  
  useEffect(() => {
    let userList: User[] = [];
    const usersCollectionRef = collection(db, 'users');
    const unsub = onSnapshot(usersCollectionRef, (querySnapshot) => {
      querySnapshot.docs.map((doc) => {
        const user: User= {
          name: doc.data().name,
          email: doc.data().email,
          age: doc.data().age,
          admin: doc.data().admin,
        };
        userList.push(user);
      });
      setUsers(userList);
    });
    return unsub;
  });

  return (
    <div>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label>名前</label>
          <input defaultValue="test" {...register('name')} />
        </div>
        <div>
          <label>年齢</label>
          <input defaultValue="test" {...register('age')} />
        </div>
        <div>
          <label>メールアドレス</label>
          <input defaultValue="test" {...register('email')} />
        </div>
        {errors.name && (
          <span>Error!!!</span>
        )}
        <input value="登録" type="submit" />
      </form>
      <div>
        {users.map((user, index) => (
          <div key={index.toString()}>{user.name}</div>
        ))}
      </div>
    </div>
  );
}

export default App;

ターミナルにて、package.josnのあるディレクトリで、下記コマンドを実行するとChromでアプリが起動する

npm run start

入力フォームに文字列を入力して登録ボタンを押下すると、Cloud FIrestoreへデータが登録され、登録されているデータが画面に出力されることを確認できる

5.React+TypeScriptアプリの画面上からデータを削除する

App.tsxの中身を少し変えてみる

kashikojin1/src/App.tsx(編集)
import { useState,useEffect }  from 'react';
import { db } from '../src/firebase';
import { addDoc, collection, onSnapshot } from 'firebase/firestore';
import { useForm, SubmitHandler } from 'react-hook-form';
import './App.css';

type User = {
  name: string;
  email: string;
  age: number;
  admin: boolean;
};

function App() {
  const [users, setUsers] = useState<User[]>([]);
  const { register,
          handleSubmit,
          watch,
          formState: { errors }
        } = useForm<User>();

  const onSubmit: SubmitHandler<User> = (data) => {
    console.log('onSubmit', data);
    const usersCollectionRef = collection(db, 'users');
    const documentRef = addDoc(usersCollectionRef, {
      name: data.name,
      email: data.email,
      age: data.age,
      admin: false,
    });
    console.log(documentRef);
  };
  
  useEffect(() => {
    let userList: User[] = [];
    const usersCollectionRef = collection(db, 'users');
    const unsub = onSnapshot(usersCollectionRef, (querySnapshot) => {
      querySnapshot.docs.map((doc) => {
        const user: User= {
          name: doc.data().name,
          email: doc.data().email,
          age: doc.data().age,
          admin: doc.data().admin,
        };
        userList.push(user);
      });
      setUsers(userList);
    });
    return unsub;
  });

  return (
    <div>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label>名前</label>
          <input defaultValue="test" {...register('name')} />
        </div>
        <div>
          <label>年齢</label>
          <input defaultValue="test" {...register('age')} />
        </div>
        <div>
          <label>メールアドレス</label>
          <input defaultValue="test" {...register('email')} />
        </div>
        {errors.name && (
          <span>Error!!!</span>
        )}
        <input value="登録" type="submit" />
      </form>
      <div>
        {users.map((user, index) => (
          <div key={index.toString()}>{user.name}</div>
        ))}
      </div>
    </div>
  );
}

export default App;

ターミナルにて、package.josnのあるディレクトリで、下記コマンドを実行するとChromでアプリが起動する

npm run start

一覧の右側にある削除ボタンを押下すると、ドキュメントIDをキーにCloud FIrestoreからデータを削除する

参考

5
4
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
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?