Firebaseについてあれこれ(Authentication, Cloud Firestore)

はじめに

 FirebaseのCloud Firestoreはバックエンドの実装を行うことなくデータベース管理ができるGoogleのサービスです。また、Authenticationは、誰でも簡単にアカウント認証やログイン機能を作ることができるサービスです。これらによってアプリケーション開発のハードルがぐんと下がります。  そんなおかげでプログラミング歴数ヶ月の素人でもwebアプリ開発ができたので(経験者からのアドバイスを多分に受けながらですが)、備忘録も兼ねて詰まったところなどを主に残そうと思います。

1. Authenticationでよくあるアカウント作成の流れを作る

Authenticationでアカウントを作成させる時、

  1. メールアドレスとパスワードを入力
  2. 認証用メールが入力したメールアドレスに届く
  3. 認証用URLを踏んで認証完了

がよくある会員登録制のアプリの基本的な動作ですが、Authenticationのメソッドには一発でそれを可能にするメソッドはありません(見つけられなかっただけかも)。そこで、メールとパスワードを入力すれば即座にアカウントが作成されるcreateUserWithEmailAndPasswordメソッドと、メールによって認証する機能を持つsendEmailVerificationメソッドの合わせ技で実装します。こんな感じです。

const handleSubmit = async (event: { preventDefault: () => any }) => {
    event.preventDefault();
    createUserWithEmailAndPassword(auth, email, password).then(() => {
      if (auth.currentUser !== null) {
        sendEmailVerification(auth.currentUser);
      }
      console.log(auth.currentUser);
    });
  };

これで上に書いたようなアカウント作成の流れが作れます。 auth.currentuserはユーザから見て自分のアカウントの情報を取得します。ここではメールアドレスやuidが入っています。 作成されたアカウントには、一位性のあるuidが発行され、紐づけられています。

2. Authenticationで作成したアカウントのuidをFirestoreのドキュメントIDと一致させたいとき

今回開発したアプリでは、アカウントを作成したら次はプロフィールを作成する流れになります。プロフィールは、Cloud Firestore上のデータベースに保存します。Firestoreのデータベースは、コレクション→ドキュメント→フィールド の順に階層構造になっています。コレクションはデータの大まかなジャンル(プロフィール、○○の履歴、△△の履歴)分け、ドキュメントはそのジャンルの中のデータの内容(プロフィールであれば名前、生年月日、職業とか)を指定し、フィールドに最終的に入力されたものを格納します。
ドキュメントには、ドキュメントIDという一意のIDが付きますが、これを自分で指定する方法と、自動生成IDに任せて付ける2通りの方法があります。プロフィールというドキュメントを作成する際、プロフィールはAuthenticationにあるアカウントと紐付かないといけません。なので、Authenticationのuidと、プロフィールのドキュメントIDを一致させるのが効果的です。そのためには、

  1. Authenticationからアカウントのuidを取得する
  2. 取得したuidをドキュメントIDとしてプロフィールを作成する

という流れが必要です。 コードにするとこうなります。

const handleCreate = async () => {
   // 入力フォームに空欄がないことを確かめる
    if (auth.currentUser !== null) {
      if (
        UserName !== "" &&
        UserNameKana !== "" &&
        UserTel !== "" &&
        UserPlan !== "" &&
        UserAdmin === false
      ) {
   // AuthenticationのuidをドキュメントIDとしてプロフィールを作成
        await setDoc(doc(db, "users", auth.currentUser.uid), {
          userName: UserName,
          userNameKana: UserNameKana,
          tel: UserTel,
          plan: UserPlan,
          admin: UserAdmin,
          addProfile: addProfile,
        });
        alert("追加できました");
      } else {
        alert("全ての欄を記入、選択してください。");
      }
    }
  };

これで一応プロフィールを作成することができます。(カナ名とかをカナ指定する処理などは未実装なので不完全ですが。。。)

次回は、

onAuthStateChangedについて説明します。(なんだそれ!)

React + Firebase (Cloud Firestore) の連携

はじめに

今回は、データベースにFirebase(Cloud Firestore)を使用して、フロントエンドと連携し、データベースを直接操作できるようにしていきたいと思います。
これを利用する利点は、バックエンドを記述する必要がなく、フロントエンド側でfirebaseライブラリーを使ってDBへの書き込み・読み込みが簡単にできることにあります。

Cloud Firestoreとは

Cloud Firestoreとは、firebase内で利用できるのNoSQLのデータベースのことです。
NoSQLデータベースというのは、非リレーショナルなデータベースを表す言葉で、「データベースの分類」を表す言葉です。
従来のデータベースはリレーショナルデータベース(RDB)と呼ばれるものであり、データを表形式で管理するデータベースです。イメージとしては、Excelの表のような形式でデータを管理するような感じです。

実際にやってみる

1. Firebaseで新規プロジェクトを立ち上げ、アプリを登録

まず最初に、Firebaseプロジェクトを作成し、そのプロジェクトにアプリを登録する必要があります。
アプリを Firebase に登録すると、Firebase プロジェクトのリソースとアプリを接続するためのコードが提供されます。ここの詳しい方法は別記事にて書こうと思います。

2. React Appとの連携

$ npx create-react-ap --template typescriptescript

で新規アプリを立ち上げたら、Firebaseをインストールするために以下のコマンドを叩きます。

$ npm install firebase

インストール出来たら、次はsrcディレクトリにfirebace.tsxというファイルを作成します。
ここにはアプリケーションの初期化処理を記述します。
Firebaseのプロジェクト概要の横にある歯車マークをクリックし、プロジェクトの設定に飛びます。
マイアプリ 内の SDKの設定と構成 で、npmにチェックを入れ、「次に Firebase を初期化し、使用するプロダクトの SDK の利用を開始します。」以下のコードをコピーします。

// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
const firebaseConfig = {
  apiKey: "XXXXXXXXXXXXXXXXXXXX",
  authDomain: "XXXXXXXXXXXXXXXX",
  projectId: "XXXX",
  storageBucket: "XXXXXXX",
  messagingSenderId: "XXXXXXXXXXXX",
  appId: "XXXXXXXXXXXXXXXXXXXXXXXXX"
};

// Initialize Firebase
const app = initializeApp(firebaseConfig);

これで連携作業は終わりです。次回からはデータベースにテストデータを入力し、ブラウザに内容を表示させるところまでをやりたいと思います。

React Hooksについて(useState, useEffect)

はじめに

今回は、React Hooks、その中の useState, useEffectについて説明してみようと思います。

React Hooksとは

そもそもReact Hooksとは何かというと、React 16.8 で追加された機能で、Reactの関数型コンポーネントのライフサイクルに応じた処理を記述することが出来る機能です。 React で提供されている基本的なHooksは以下のものがあります。

  • useState
  • useEffect
  • useContext
  • useMemo
  • useCallback

1. useState

useState は、関数型コンポーネントで状態(state)を管理するためのHookです。
以下は、useStateを使う時の基本的なコードです。

const [state, setState] = useState("");

useStateは、状態(state)の変数と状態(state)を更新(追加ではない)するための関数を返します。
状態(state)の更新をする際は、必ず更新用の関数(この場合は setState)を用いて行う必要があります。
また、上記の中のsetState("")中の( )内には状態の初期値が入ります。

2. useEffect

useEffect は、関数型コンポーネントで副作用を実行するためのHookです。副作用とは、コンポーネント内での外部データの取得などの処理のことを指します。
以下は、useEffectを使う時の基本的なコードです。

useEffect(() => {
  // 副作用処理
  
  return () => {
    // 返り値の入力
  };
}, []);

useEffectでは副作用となる処理を関数の中に記述します。return で関数を返す処理を記述します。
また、第2引数を指定することによって副作用処理のトリガーとすることが出来ます。第2引数は、関数の最後にある[]の中に記述します。

3. useState, useEffect を用いたユースケース

useState, useEffect を用いて、たばこの銘柄を検索するアプリを作りました。デザインは何もしていません。

  • 検索ワードでの検索機能
  • 生産国でのフィルタリング
  • フレーバーでのフィルタリング

が出来るようになっています。

以下にソースコードを載せておきます

const App: React.FC = () => {
  // 検索先となるたばこデータを定義
  const tobacoLists = [
    {
      title: "マールボロ",
      country: "海外製",
      flavor: "レギュラーたばこ",
      tar: "12",
    },
    {
      title: "マールボロ・メンソール",
      country: "海外製",
      flavor: "メンソールたばこ",
      tar: "12",
    },
    
    {
      title: "セブンスター",
      country: "日本製",
      flavor: "レギュラーたばこ",
      tar: "14",
    },
    {
      title: "セブンスター・メンソール",
      country: "日本製",
      flavor: "メンソールたばこ",
      tar: "12",
    },
  ];

  // 生産国、フレーバーでソートするための検索条件を定義
  const countries = ["日本製", "海外製"];
  const flavors = ["レギュラーたばこ", "メンソールたばこ"];

  // useStateの定義
  // tobacoLists型の変数tobacosを定義
  const [tobacos, setTobacos] = useState(tobacoLists);
  // 検索窓に入力された文字列の変数keywordを定義
  const [keyword, setKeyword] = useState("");
  // countries型の変数selectCountryを定義
  const [selectCountry, setSelectCountry] = useState("");
  // flavors型の変数selectFlavorsを定義
  const [selectFlavor, setSelectFlavor] = useState("");
  // 検索結果を表示するための変数filteredTobacosを定義
  const [filteredTobacos, setFilteredTobacos] = useState(tobacoLists);

  // useEffect
  useEffect(() => {
    // 副作用処理
    // 検索窓内の文字列が空文字列である(何も入力されていない)時の処理
    if (keyword === "") {
      const result = tobacos.filter((e) => {
        return (
          // countory, flavor の要素が含まれているかを返す
          e.country.includes(selectCountry) && e.flavor.includes(selectFlavor)
        );
      });
      setFilteredTobacos(result);
    } 
    // 検索窓内に何か文字列が入力された時の処理
    else if (keyword !== "") {
      const result = tobacos.filter((e) => {
        return (
          // keyword, countory, flavor の要素が含まれているかを返す
          e.title.includes(keyword) &&
          e.country.includes(selectCountry) &&
          e.flavor.includes(selectFlavor)
        );
      });
      setFilteredTobacos(result);
    }
    // []内に第2引数を指定することで副作用処理のトリガーとする
    // 複数の第2引数を指定可能
  }, [keyword, selectCountry, selectFlavor]);

  return (
    // 検索画面
    <div className="App">
      <h1>たばこ銘柄検索</h1>
      <input
        type="text"
        name="title"
        value={keyword}
        placeholder="検索ワードを入力"
        onChange={(e) => setKeyword(e.target.value)}
      />
      <select name="country" onChange={(e) => setSelectCountry(e.target.value)}>
        {<option value="">生産国を選択</option>  /*空文字列のvalueを設定することで入力値の初期化を可能にする() */}
        {countries.map((e, i) => {
          return <option key={i}>{e}</option>;
        })}
      </select>
      <select name="flavor" onChange={(e) => setSelectFlavor(e.target.value)}>
        {<option value="">フレーバーを選択</option> /*空文字列のvalueを設定することで入力値の初期化を可能にする() */}
        {flavors.map((e, i) => {
          return <option key={i}>{e}</option>;
        })}
      </select>

      {/* 検索結果 */}
      <table>
        <thead>
          <tr>
            <th>銘柄</th>
            <th>生産国</th>
            <th>フレーバー</th>
            <th>タール</th>
          </tr>
        </thead>
        <tbody>
          {/* 検索結果をmap表示 */}
          {filteredTobacos.map((tobacos) => {
            return (
              <tr key={tobacos.title}>
                <td>{tobacos.title}</td>
                <td>{tobacos.country}</td>
                <td>{tobacos.flavor}</td>
                <td>{tobacos.tar}mg</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
};

export default App;

今回はすべてApp.tsx内に記述しました。 たばこデータのオブジェクトを追加することで検索先の銘柄を増やすことが出来ます。次回は、これにたばこデータの追加、編集機能を追加しようと思います。

はじめました。

はじめに

Reactの技術ブログをはじめました。
これから日記的に学習したことを残していこうと思います。  

React 開発環境構築

ここでは、まずReactの開発環境の構築について説明します。
全体の流れとしては

  1. Node.js のインストール
  2. create-react-app で新規アプリの立ち上げ
  3. ローカルホストで初期画面を表示させる

なので、今回はここまでを説明していこうと思います。

1. Node.js のインストール

下記のURLからNode.jsのダウンロードサイトに飛びます。

https://nodejs.org/ja/

開くとLTS版と最新版の二つがあります。
LTS版とは、Long Time Support の略で、長期のサポートが受けられるものです。
今回はLTS版をダウンロードします。
ダウンロードが出来て、インストールまで済んだら、ターミナルを開いてNodeがインストールできているか確認します。

$ node -v
v16.16.0

という風にバージョンが表示されていれば成功です。

2. create-react-appで新規アプリの立ち上げ

Nodeがインストール出来たら、新規アプリを立ち上げてみましょう。
ターミナルでアプリを作るディレクトリに移動して、下記のコマンドを入力します。
windowsの場合

$ npx create-react-app <アプリ名>

Macの場合

$ create-react-app <アプリ名>

実行すると、<アプリ名>というフォルダが出来てその中にガガガッと色んなファイルが入っていきます。
ちなみに上記の立ち上げコマンドはJavaScriptで立ち上げる用のコマンドで、TypeScriptを使う場合は
windows

$ npx create-react-app <アプリ名> --template typescript

Mac

$ create-react-app <アプリ名> --template typescript

というようにアプリ名の後に --template typescript と入力すればTypeScriptでのアプリの立ち上げが可能です。

3. ローカルホストで初期画面を表示させる

正常にアプリが立ち上がると、ターミナルにこのように表示されます

Success! Created <アプリ名> at \XXXX\YYYY\ZZZZ\<アプリ名> Inside that directory, you can run several commands:
 npm start
  Starts the development server.
 npm run build
  Bundles the app into static files for production.
 npm test
  Starts the test runner.  npm run eject
  Removes this tool and copies build dependencies, configuration files
  and scripts into the app directory. If you do this, you can’t go back!
We suggest that you begin by typing:
 cd testapp
 npm start
Happy hacking!

上記の npm XXXX コマンドは、アプリを操作するためのコマンドで、

  • npm start
    ローカルホストを用いてアプリを実行します。ブラウザで動作確認できます。
  • npm run build
    アプリのビルドを行います。実際にWebにアップロードするファイルを生成します。
  • npm test
    テストプログラムを実行して、アプリのテストを行います。
  • npm run eject
    アプリの依存関係をアプリ内に移動させ、完全に独立させた形で扱えるようにします。

それでは、ローカルホストでアプリを実行してみましょう。
ターミナルで先ほど作成された<アプリ名>ディレクトリに移動して、以下のコマンドを入力します。

$ npm start

ブラウザに

が表示されれば成功です!

まとめ

今回は、Reactの開発環境の構築と、新規アプリの立ち上げまでの手順をまとめました。今後もインプットしたことをブログにアウトプットしていこうと思います!