RalaCode
menusearch

ReactとFirebaseで認証機能を実装する方法【作りながら解説】

  • React
  • Firebase
ReactとFirebaseで認証機能を実装する方法【作りながら解説】

「Reactでログイン機能を実装できたらいいな」

今回は上記の悩みにお答えします。

ログイン機能は「認証機能」とも言います。
Reactでは認証機能を実装できます。

ただ、それにはかなり難しいコードを書くことになります。
管理も面倒...。

そこで、便利なのが「Firebase」です。
Firebaseを使うことで、以下が可能。

  • Reactアプリに認証機能を実装できる

  • 認証方法はメールアドレスだけでなくSNSアカウントも利用可能

  • ユーザーの管理も簡単

というわけで、この記事ではReactとFirebaseで認証機能を実装する方法を徹底解説していきます。

解説はサンプルアプリを作りながら進める形となっております。
サンプルアプリの機能は以下。

  • ユーザー登録(マイページへリダイレクト)

  • ログインする(失敗したら警告、成功ならマイページへリダイレクト)

  • マイページ表示(ユーザーのメールアドレスが表示される)

  • ログアウトする(ログインページにリダイレクト)

※ FirebaseならSNSアカウントでログインできる機能も実装可能ですが、この記事ではメールアドレスとパスワードのみで認証を行います。ご了承ください。

では始めていきます。

Firebaseに登録する

以下の記事にて、Firebaseに登録する手順を解説しております。

【Firebase】登録手順と使い方【わかりやすく解説】
【Firebase】登録手順と使い方【わかりやすく解説】
記事を読む

上記の記事を参考に、以下をやっていただくとスムーズです。

  • プロジェクトを作成

  • プロジェクトにアプリを追加する

このコードを使います

こちらの画面で表示されるコードを使います。

コードの内容は、人によって違うはず。
ご自分のコードをご確認ください。

※ この記事で扱うのはFirebase Authentication v9です。ネットの情報を見ていると、v9以前のやり方で解説されている記事がまだ散見されます。しかし現在では、それだと上手くいかない可能性が高いです。ご注意を。

Authenticationを設定

Firebaseで少し設定を行います。

「Authentication」をクリック

こちらの画面で「Authentication」をクリックします。
Authenticationは日本語で「認証」です。

「始める」をクリック

始める」をクリックしましょう。

「メール/パスワード」をクリック

色んなログイン方法を実装できるようですね。
ここでは「メール/パスワード」をクリックします。

メール/パスワードによるログインを有効にして「保存」をクリック

「メール/パスワード」によるログインを有効にして、「保存」をクリックです。

「メール/パスワード」によるログインの設定が完了

これで設定は完了です。

コンポーネントを作る

では、Reactアプリを作っていきます。
まずは、コンポーネントを用意しましょう。

用意するコンポーネントは以下です。

  • ユーザー登録ページ

  • ログインページ

  • マイページ

新規登録ページとして、以下のような「Register.js」を作ります。

/* Register.js */

import React from "react";

const Register = () => {
  return (
    <>
      <h1>新規登録</h1>
      <form>
        <div>
          <label>メールアドレス</label>
          <input
            name="email"
            type="email"
          />
        </div>
        <div>
          <label>パスワード</label>
          <input
            name="password"
            type="password"
          />
        </div>
        <button>登録する</button>
      </form>
    </>
  );
};

export default Register;

次に、ログインページとして「Login.js」を作ります。

/* Login.js */

import React from "react";

const Login = () => {
  return (
    <>
      <h1>ログインページ</h1>
      <form>
        <div>
          <label>メールアドレス</label>
          <input
            name="email"
            type="email"
          />
        </div>
        <div>
          <label>パスワード</label>
          <input
            name="password"
            type="password"
          />
        </div>
        <button>ログイン</button>
      </form>
    </>
  );
};

export default Login;

そして、マイページが「Mypage.js」です。

/* Mypage.js */

import React from "react";

const Mypage = () => {
  return (
    <>
      <h1>マイページ</h1>
      <button>ログアウト</button>
    </>
  );
};

export default Mypage;
「Register.js」、「Login.js」、「Mypage.js」を作った

今のところ、プロジェクトの中身は上記の画像の通りです。

React Routerを設定する

次に、「App.js」を編集していきます。

「App.js」では「React Router」を設定します。
これにより、先ほど作ったコンポーネントをページとして表示できます。

「React Router」を使うには、「react-router-dom」というライブラリをインストールします。
ターミナルに以下のように記述して実行します。

$ npm install --save react-router-dom

または

$ yarn add react-router-dom

そして、「App.js」には以下のように記述します。

/* App.js */

import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Register from "./Register";
import Login from "./Login";
import Mypage from "./Mypage";

const App = () => {
  return (
    <div className="container">
      <BrowserRouter>
        <Routes>
          <Route path={`/register/`} element={<Register />} />
          <Route path={`/login/`} element={<Login />} />
          <Route path={`/`} element={<Mypage />} />
        </Routes>
      </BrowserRouter>
    </div>
  );
};

export default App;

これで「React Router」を設定できました。

cssで見た目も整えておきましょう。
あくまで例ですが、「index.css」には以下のように記述します。

/* index.css */

* {
  margin: 0;
  padding: 0;
}

body {
  font-family: sans-serif;
  background-color: #f7f7f7;
}

.container {
  display: grid;
  row-gap: 30px;
  width: 84%;
  max-width: 640px;
  margin: 60px auto;
}

.container h1 {
  justify-self: center;
}

.container p {
  justify-self: center;
}

form {
  display: grid;
  row-gap: 30px;
}

input {
  width: 100%;
  box-sizing: border-box;
  border: solid 1px #ddd;
  padding: 10px;
  font-size: 16px;
  outline: none;
  appearance: none;
  border-radius: 0;
}

button {
  justify-self: center;
  border: none;
  background-color: #0d6efd;
  color: #fff;
  padding: 10px;
  font-size: 16px;
}

button:hover {
  cursor: pointer;
  opacity: 0.7;
}

では、開発サーバーを起動してみましょう。

$ npm start
フロントエンドの見た目はこんな感じ

アプリの見た目は、こんな感じになってるかと思います。

以下の3つのURLでページが表示されます。

  • localhost:3000/register/

  • localhost:3000/login/

  • localhost:3000

Firebaseの設定ファイルを作る

次に、Firebaseのパッケージをインストールします。

$ npm install --save firebase

または

$ yarn add firebase

そして、Firebaseの設定ファイル「FirebaseConfig.js」を作って以下のように記述します。

/* FirebaseConfig.js */

import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";

const firebaseConfig = {
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_APP_ID
};

const app = initializeApp(firebaseConfig);

export const auth = getAuth(app);
このコードを使います

記事冒頭でも示したこちらの画面で表示されたコードを貼り付けただけです。

ただし、今回は「環境変数」を使いました。
プロジェクトのルートディレクトリに「.env」というファイルを作り、以下のように記述します。

# .env

REACT_APP_FIREBASE_API_KEY=AIzaSyBMrjoRemf0nalP4G7cXAPWiyRDh0C5diY
REACT_APP_FIREBASE_AUTH_DOMAIN=gatsby-firebase2.firebaseapp.com
REACT_APP_FIREBASE_PROJECT_ID=gatsby-firebase2
REACT_APP_FIREBASE_STORAGE_BUCKET=gatsby-firebase2.appspot.com
REACT_APP_FIREBASE_MESSAGING_SENDER_ID=956256786332
REACT_APP_FIREBASE_APP_ID=1:956256786332:web:945a152c66dc7c86f21a59

変数の値は、Firebaseで取得したご自分のものに設定してください。
上記のコードは、あくまで例であり使用はできません。

また、React開発で環境変数を使うのはセキュリティ的にも重要です。
環境変数の使い方は、以下の記事にて解説しておりますので合わせてどうぞ。

Reactで環境変数を読み込む【開発・本番で切り替え可能】
Reactで環境変数を読み込む【開発・本番で切り替え可能】
記事を読む

「FirebaseConfig.js」の上から2行目と、最後の行の記述は付け加えました。
以下のコードです。

import { getAuth } from "firebase/auth";
export const auth = getAuth(app);

これは、Firebaseの認証機能を使う場合に必要な記述です。

では、次に進んでいきます。
環境変数を設定したので、ここで開発サーバーを一度停止してもう一度起動してください。

入力した情報を取得する

次に実装するのは「inputに入力された値を取得する設定」です。

  • ユーザー登録するとき

  • ログインするとき

上記の際、メールアドレスパスワードが入力されるはずです。
それらを情報として取得します。

方法は色々ありますが、ここでは「useState」を使います。

inputタグを含んでいる、以下の2つのコンポーネントを編集していきます。

  • Register.js

  • Login.js

/* Register.js */

/* useStateをimport↓ */
import React, { useState } from "react";

const Register = () => {
  /* ↓state変数を定義 */
  const [registerEmail, setRegisterEmail] = useState("");
  const [registerPassword, setRegisterPassword] = useState("");

  return (
    <>
      <h1>新規登録</h1>
      <form>
        <div>
          <label>メールアドレス</label>
          {/* ↓「value」と「onChange」を追加 */}
          <input
            name="email"
            type="email"
            value={registerEmail}
            onChange={(e) => setRegisterEmail(e.target.value)}
          />
        </div>
        <div>
          <label>パスワード</label>
          {/* ↓「value」と「onChange」を追加 */}
          <input
            name="password"
            type="password"
            value={registerPassword}
            onChange={(e) => setRegisterPassword(e.target.value)}
          />
        </div>
        <button>登録する</button>
      </form>
    </>
  );
};

export default Register;
/* Login.js */

/* useStateをimport↓ */
import React, { useState } from "react";

const Login = () => {
  /* ↓state変数を定義 */
  const [loginEmail, setLoginEmail] = useState("");
  const [loginPassword, setLoginPassword] = useState("");

  return (
    <>
      <h1>ログインページ</h1>
      <form>
        <div>
          <label>メールアドレス</label>
          {/* ↓「value」と「onChange」を追加 */}
          <input
            name="email"
            type="email"
            value={loginEmail}
            onChange={(e) => setLoginEmail(e.target.value)}
          />
        </div>
        <div>
          <label>パスワード</label>
          {/* ↓「value」と「onChange」を追加 */}
          <input
            name="password"
            type="password"
            value={loginPassword}
            onChange={(e) => setLoginPassword(e.target.value)}
          />
        </div>
        <button>ログイン</button>
      </form>
    </>
  );
};

export default Login;

一番上の行で、useStateをimportしています。

そして、state変数を定義してそれぞれinputタグに「value」と「onChange」を組み込んでいます。

これは、useStateを使ってinputで入力された値を取得するときのお決まりの流れです。
このあたり、もっと詳しく知りたい方には以下の記事もおすすめです。

【React】inputに入力された値(value)を取得する
【React】inputに入力された値(value)を取得する
記事を読む

「ユーザー登録」を実装する

では、以下の操作によりユーザー登録が行われる機能を実装していきます。

  • メールアドレスを入力する

  • パスワードを入力する

  • 「登録する」ボタンをクリックする

「Register.js」を編集していきます。

/* Register.js */

import React, { useState } from "react";
/* ↓「createUserWithEmailAndPassword」と「auth」をimport */
import { createUserWithEmailAndPassword } from "firebase/auth";
import { auth } from "./FirebaseConfig.js";

const Register = () => {
  const [registerEmail, setRegisterEmail] = useState("");
  const [registerPassword, setRegisterPassword] = useState("");

  /* ↓関数「handleSubmit」を定義 */
  const handleSubmit = async (e) => {
    e.preventDefault();

    try {
      await createUserWithEmailAndPassword(
        auth,
        registerEmail,
        registerPassword
      );
    } catch(error) {
      alert("正しく入力してください");
    }
  };

  return (
    <>
      <h1>新規登録</h1>
      {/* ↓「onSubmit」を追加 */}
      <form onSubmit={handleSubmit}>
        <div>
          <label>メールアドレス</label>
          <input
            name="email"
            type="email"
            value={registerEmail}
            onChange={(e) => setRegisterEmail(e.target.value)}
          />
        </div>
        <div>
          <label>パスワード</label>
          <input
            name="password"
            type="password"
            value={registerPassword}
            onChange={(e) => setRegisterPassword(e.target.value)}
          />
        </div>
        <button>登録する</button>
      </form>
    </>
  );
};

export default Register;

コードの上の方で以下の2つをimportしています。

  • createUserWithEmailAndPassword

  • auth

createUserWithEmailAndPassword」はFirebaseで用意された関数です。
auth」は「FirebaseConfig.js」で定義したものです。

そして、関数「handleSubmit」を定義しています。

const handleSubmit = async (e) => {
  e.preventDefault();

  try {
    await createUserWithEmailAndPassword(
      auth,
      registerEmail,
      registerPassword
    );
  } catch(error) {
    alert("正しく入力してください");
  }
};

これを実行するだけで、ユーザー登録が可能。

関数の中に「e.preventDefault();」というのがあります。
これがないと関数「handleSubmit」が実行されたときにブラウザがリロードされてしまい、ちょっと都合が悪いんですよね。

あとは、「登録する」ボタンをクリックしたときに関数「handleSubmit」を実行するように、「onSubmit」をformタグに追加します。

<form onSubmit={handleSubmit}>
  ...略...
</form>
試しにユーザー登録してみる

試しにユーザー登録してみましょう。

メールアドレスは架空のもので大丈夫です。
メールアドレスとパスワードを入力して「登録する」をクリックします。

「登録する」をクリックする代わりに、キーボードの「Enter」でもいけます。

今のところ、以下の状態です。

  • ユーザー登録に失敗したら「正しく入力してください」というalertが表示される

  • ユーザー登録に成功したら、特に何も表示されない

ユーザー登録に失敗する理由は主に以下です。

  • メールアドレスに「@」がないとき

  • メールアドレスの@以下の部分がおかしいとき

  • パスワードが5文字以下のとき

  • すでに登録してあるユーザー情報の場合

  • 空白のまま登録しようとしたとき

  • などなど

本当はもっといっぱいありますが、一部だけ紹介すると上記の通りです。

このあたりの設定は全てFirebaseがやってくれているわけです。
これを自分でコーディングしようとすると相当しんどいんですよね...。

Firebaseの管理画面でユーザーが追加されている

ここで、Firebaseの管理画面を見てみましょう。

上の画面で、「Users」のタブに切り替えます。
再読み込みすると、先ほど登録したユーザーが追加されているかと思います。

ユーザーの右側の方に表示されている、「点が3つ並んだところ」をクリックすると以下の操作ができます。

  • パスワードを再設定

  • アカウントを無効にする

  • アカウントを削除

「ログイン状態」を監視する

ユーザー登録したら、「ログインした状態」となります。

ログインしたら、自動的にマイページを表示したいわけです。
そのためには以下のようにします。

  • ログインしているかどうかを判定する

  • ログインしていればマイページを表示する

「Register.js」に記述を追加していきます。

/* Register.js */

/* 「useEffect」をimport↓ */
import React, { useState, useEffect } from "react";
/* ↓「onAuthStateChanged」をimport */
import {
  createUserWithEmailAndPassword,
  onAuthStateChanged
} from "firebase/auth";
import { auth } from "./FirebaseConfig.js";
/* ↓「Navigate」をimport */
import { Navigate } from "react-router-dom";

const Register = () => {
  const [registerEmail, setRegisterEmail] = useState("");
  const [registerPassword, setRegisterPassword] = useState("");

  const handleSubmit = async (e) => {
    ......
  };

  /* ↓state変数「user」を定義 */
  const [user, setUser] = useState("");

  /* ↓ログインしているかどうかを判定する */
  useEffect(() => {
    onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
    });
  }, []);

  return (
    <>
      {/* ↓ログインしていればマイページを表示 */}
      {user ? (
        <Navigate to={`/`} />
      ) : (
        <>
          <h1>新規登録</h1>
          <form onSubmit={handleSubmit}>
            <div>
              <label>メールアドレス</label>
              <input
                name="email"
                type="email"
                value={registerEmail}
                onChange={(e) => setRegisterEmail(e.target.value)}
              />
            </div>
            <div>
              <label>パスワード</label>
              <input
                name="password"
                type="password"
                value={registerPassword}
                onChange={(e) => setRegisterPassword(e.target.value)}
              />
            </div>
            <button>登録する</button>
          </form>
        </>
      )}
    </>
  );
};

export default Register;

まず、以下の3つを新たにimportしています。

  • useEffect

  • onAuthStateChanged

  • Navigate

onAuthStateChanged」はFirebaseが用意してくれている関数です。

そして、以下の記述でログインしているかどうかを判定しています。

const [user, setUser] = useState("");

useEffect(() => {
  onAuthStateChanged(auth, (currentUser) => {
    setUser(currentUser);
  });
}, []);

ログイン判定のレンダリングは1度だけでいいので、「useEffect」を使っています。

そして、以下の通りです。

  • 「user」が値を持てば、ログインしている状態

  • 「user」に値がなければ、ログインしていない状態

これを利用して、ログインしていれば自動でマイページを表示するように設定します。

{user ? (
  <Navigate to={`/`} />
) : (
  <>
    <h1>新規登録</h1>

    ...略...

  </>
)}

三項演算子を使った書き方です。

さらに、「Navigate」を使えば指定したページにリダイレクトさせることができます。

ユーザー情報を表示する

今の状況は以下です。

  • ユーザー登録した(と同時にログイン状態となった)

  • 自動的にマイページが表示された

ユーザー登録ページからリダイレクトされ、今はマイページが表示された状態かと思います。

ここで、マイページにユーザーのメールアドレスを表示してみましょう。
Mypage.js」を編集していきます。

/* Mypage.js */

/* 「useState」と「useEffect」をimport↓ */
import React, { useState, useEffect } from "react";
/* 「onAuthStateChanged」と「auth」をimport↓ */
import { onAuthStateChenged } from "firebase/auth";
import { auth } from "./FirebaseConfig.js";

const Mypage = () => {
  /* ↓state変数「user」を定義 */
  const [user, setUser] = useState("");

  /* ↓ログインしているかどうかを判定する */
  useEffect(() => {
    onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
    });
  }, []);

  return (
    <>
      <h1>マイページ</h1>
      {/* ↓ユーザーのメールアドレスを表示(ログインしている場合) */}
      <p>{user?.email}</p>
      <button>ログアウト</button>
    </>
  );
};

export default Mypage;

新たに、以下をimportしました。

  • useState

  • useEffect

  • onAuthStateChanged

  • auth

そして、「Register.js」と同じようにログインしているかどうかを判定する設定を追加しました。
ログインしていれば、state変数「user」にユーザー情報が代入されます。

さらに、ユーザーのメールアドレスを表示するように、pタグを使って以下のように記述しています。

<p>{user?.email}</p>

{/* 略さずに書くとこちら↓ */}
<p>{user && user.email}</p>

三項演算子を使いました。
略さない形は、上記の通りです。

マイページにユーザーのメールアドレスが表示される

上の画像のように、マイページにユーザーのメールアドレスが表示されます。

「ログアウト」を実装する

次は、以下を実装します。

  • 「ログアウト」ボタンをクリックする

  • ログアウトが実行され、ログインページにリダイレクトする

「Mypage.js」を編集していきます。

/* Mypage.js */

import React, { useState, useEffect } from "react";
/* 「signOut」をimport↓ */
import { onAuthStateChenged, signOut } from "firebase/auth";
import { auth } from "./FirebaseConfig.js";
/* ↓「useNavigate」をimport */
import { useNavigate } from "react-router-dom";

const Mypage = () => {
  const [user, setUser] = useState("");

  useEffect(() => {
    onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
    });
  }, []);

  /* ↓「navigate」を定義 */
  const navigate = useNavigate();

  /* ↓関数「logout」を定義 */
  const logout = async () => {
    await signOut(auth);
    navigate("/login/");
  }

  return (
    <>
      <h1>マイページ</h1>
      <p>{user?.email}</p>
      {/* ↓「onClick」を追加 */}
      <button onClick={logout}>ログアウト</button>
    </>
  );
};

export default Mypage;

コードの上の方では、「signOut」をimportしています。
これも、Firebaseが用意してくれている関数です。

そして、「navigate」と関数「logout」を定義しました。

const navigate = useNavigate();

const logout = async () => {
  await signOut(auth);
  navigate("/login/");
};

関数「logout」を実行すると、以下のことが起こります。

  • ログアウトが行われる

  • ログインページにリダイレクトされる

そして、この関数「logout」を「ログアウト」ボタンをクリックすることで実行するように設定します。

<button onClick={logout}>ログアウト</button>
「ログアウト」をクリックするとログインページにリダイレクトされる

試しに、「ログアウト」をクリックしてみます。
すると、自動でログインページにリダイレクトされます。

「ログイン」を実装する

次は「ログイン」ですね。
「Login.js」を編集していきます。

形としては、「Register.js」とほぼ同じです。

/* Login.js */

/* ↓新たに5つimportしています */
import React, { useState, useEffect } from "react";
import {
  signInWithEmailAndPassword,
  onAuthStateChanged
} from "firebase/auth";
import { auth } from "./FirebaseConfig.js";
imoprt { Navigate } from "react-router-dom";

const Login = () => {
  const [loginEmail, setLoginEmail] = useState("");
  const [loginPassword, setLoginPassword] = useState("");

  /* ↓関数「handleSubmit」を定義 */
  const handleSubmit = async (e) => {
    e.preventDefault();

    try {
      await signInWithEmailAndPassword(
        auth,
        loginEmail,
        loginPassword
      );
    } catch(error) {
      alert("メールアドレスまたはパスワードが間違っています");
    }
  };

  /* ↓ログインを判定する設定 */
  const [user, setUser] = useState();

  useEffect(() => {
    onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
    });
  });

  return (
    <>
      {/* ↓ログインしている場合、マイページにリダイレクトする設定 */}
      {user ? (
        <Navigate to={`/`} />
      ) : (
        <>
          <h1>ログインページ</h1>
          {/* onSubmitを追加↓ */}
          <form onSubmit={handleSubmit}>
            ...略...
          </form>
        </>
      )}
    </>
  );
};

export default Login;

新たに以下をimportしました。

  • useEffect

  • signInWithEmailAndPassword

  • onAuthStateChanged

  • auth

  • Navigate

関数「handleSubmit」を定義して、その中で「signInWithEmailAndPassword」を実行することでログインを行っています。

そして、「onAuthStateChanged」でログインしているかどうかを監視しています。

ログインすると、自動的にマイページにリダイレクトされます。

ログインするとマイページにリダイレクトされる

では、試しにログインしてみましょう。

ユーザー登録したときと同じ情報を入力して、「ログイン」をクリックします。

すると、自動でマイページにリダイレクトされます。
そして、マイページにはユーザーのメールアドレスが表示されます。

アクセスを制限する

ここまでで、以下の機能がそろいました。

  • ユーザー登録(マイページにリダイレクト)

  • ログイン(マイページにリダイレクト)

  • マイページでユーザーのメールアドレスを表示する

  • ログアウト(ログインページにリダイレクト)

機能としては十分かもしれません。
ですが、以下の設定も必要です。

  • ログインしていない場合、マイページにはアクセスできないようにする

  • ログインしている場合、ユーザー登録ページログインページにはアクセスできないようにする

つまり、アクセス制限を設定するわけです。

ここまでで、「Register.js」と「Login.js」ではすでにアクセス制限を設定してあります。
ログインしている場合、自動的にマイページにリダイレクトするようにしました。

そのため、あとは「Mypage.js」にアクセス制限の設定をすればオッケーです。

/* Mypage.js */

import React, { useState, useEffect } from "react";
import { onAuthStateChenged, signOut } from "firebase/auth";
import { auth } from "./FirebaseConfig.js";
/* ↓「Navigate」をimport */
import {
  useNavigate,
  Navigate
} from "react-router-dom";

const Mypage = () => {
  const [user, setUser] = useState("");

  useEffect(() => {
    onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
    });
  }, []);

  ......

  return (
    <>
      {/* ↓ログインしていない場合はログインページにリダイレクトする設定 */}
      {!user ? (
        <Navigate to={`/login/`} />
      ) : (
        <>
          <h1>マイページ</h1>
          <p>{user?.email}</p>
          <button onClick={logout}>ログアウト</button>
        </>
      )}
    </>
  );
};

export default Mypage;

Navigate」を使って、ログインしていない場合はログインページにリダイレクトするように設定しました。

ログインしていない場合」なので、「!user」となっていることに注意です。

ちゃんと動作するか、確認してみましょう。

今ログインしている場合は、一度ログアウトしてください
ログアウトすると、自動的にログインページが表示されるはずです。

そこで、「localhost:3000」にアクセスしてみてください。
すると、「localhost:3000/login/」にリダイレクトされると思います。

ログイン判定を待機する

リダイレクトも設定しましたが、実はまだ不十分です。

試しに、またログインしてみてください。
以下の減少が起こるはずです。

  • 何も表示されない

  • 表示がちらつく

要するに、表示がおかしくなったと思います。

この原因は、「ログイン判定には時間がかかるから」です。

ログインすると、マイページにリダイレクトします。
マイページで、ログイン判定が行われます。

しかし、ログイン判定にはある程度時間がかかります。
ログイン判定が終了する前に、先にリダイレクトが実行されてしまうわけです。

リダイレクトが実行されると、ログインページに遷移します。
でもすでにログインしている状態なので、ログインページからマイページへのリダイレクトも実行されてしまいます。

よって、表示がおかしくなります。

これを、防ぐにはログイン判定が終わるまでリダイレクトさせないようにすればいいです。

では、「Mypage.js」を編集します。

/* Mypage.js */

import React, { useState, useEffect } from "react";
import { onAuthStateChenged, signOut } from "firebase/auth";
import { auth } from "./FirebaseConfig.js";
import {
  useNavigate,
  Navigate
} from "react-router-dom";

const Mypage = () => {
  const [user, setUser] = useState("");
  /* ↓state変数「loading」を定義 */
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
      /* ↓追加 */
      setLoading(false);
    });
  }, []);

  ......

  return (
    <>
      {/* ↓「loading」がfalseのときにマイページを表示する設定 */}
      {!loading && (
        <>
          {!user ? (
            <Navigate to={`/login/`} />
          ) : (
            <>
              <h1>マイページ</h1>
              <p>{user?.email}</p>
              <button onClick={logout}>ログアウト</button>
            </>
          )}
        </>
      )}
    </>
  );
};

export default Mypage;

state変数「loading」を定義しており、初期値はtrueです。

そして、関数「onAuthStateChanged」の中で「setLoading(false);」を含めました。
これにより、ログイン判定が終わったタイミングで「loading」はfalseに変わります。

「&&」は、三項演算子の書き方です。
!loading &&」、つまり「loadingがfalseのときのみ」以下が実行されます。

{!user ? (
  <Navigate to={`/login/`} />
) : (
  <>
    <h1>マイページ</h1>
    <p>{user?.email}</p>
    <button onClick={logout}>ログアウト</button>
  </>
)}

これで、ログイン判定より先にリダイレクトが実行されることはなくなります。
問題なくログインできるようになったと思います。

最後の仕上げ

では、最後の仕上げと最終完成版のコードを載せたいと思います。

仕上げと言っても、やることは以下の2つです。

  • ユーザー登録ページに、「ログインページへのリンク」を追加する

  • ログインページに、「ユーザー登録ページへのリンク」を追加する

上記の2つを行って、「Register.js」と「Login.js」は最終完成版となります。

/* Register.js(完成版) */

import React, { useState, useEffect } from "react";
import {
  createUserWithEmailAndPassword,
  onAuthStateChanged
} from "firebase/auth";
import { auth } from "./FirebaseConfig.js";
/* 「Link」をimport↓ */
import { Navigate, Link } from "react-router-dom";

const Register = () => {
  const [registerEmail, setRegisterEmail] = useState("");
  const [registerPassword, setRegisterPassword] = useState("");

  const handleSubmit = async (e) => {
    e.preventDefault();

    try {
      await createUserWithEmailAndPassword(
        auth,
        registerEmail,
        registerPassword
      );
    } catch(error) {
      alert("正しく入力してください");
    }
  };

  const [user, setUser] = useState("");

  useEffect(() => {
    onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
    });
  }, []);

  return (
    <>
      {user ? (
        <Navigate to={`/`} />
      ) : (
        <>
          <h1>新規登録</h1>
          <form onSubmit={handleSubmit}>
            <div>
              <label>メールアドレス</label>
              <input
                name="email"
                type="email"
                value={registerEmail}
                onChange={(e) => setRegisterEmail(e.target.value)}
              />
            </div>
            <div>
              <label>パスワード</label>
              <input
                name="password"
                type="password"
                value={registerPassword}
                onChange={(e) => setRegisterPassword(e.target.value)}
              />
            </div>
            <button>登録する</button>
            {/* ↓リンクを追加 */}
            <p>ログインは<Link to={`/login/`}>こちら</Link></p>
          </form>
        </>
      )}
    </>
  );
};

export default Register;
/* Login.js(完成版) */

import React, { useState, useEffect } from "react";
import {
  signInWithEmailAndPassword,
  onAuthStateChanged
} from "firebase/auth";
import { auth } from "./FirebaseConfig.js";
/* 「Link」をimport↓ */
imoprt { Navigate, Link } from "react-router-dom";

const Login = () => {
  const [loginEmail, setLoginEmail] = useState("");
  const [loginPassword, setLoginPassword] = useState("");

  const handleSubmit = async (e) => {
    e.preventDefault();

    try {
      await signInWithEmailAndPassword(
        auth,
        loginEmail,
        loginPassword
      );
    } catch(error) {
      alert("メールアドレスまたはパスワードが間違っています");
    }
  };

  const [user, setUser] = useState();

  useEffect(() => {
    onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
    });
  });

  return (
    <>
      {user ? (
        <Navigate to={`/`} />
      ) : (
        <>
          <h1>ログインページ</h1>
          <form onSubmit={handleSubmit}>
            <div>
              <label>メールアドレス</label>
              <input
               name="email"
               type="email"
               value={loginEmail}
               onChange={(e) => setLoginEmail(e.target.value)}
              />
            </div>
            <div>
              <label>パスワード</label>
              <input
                name="password"
                type="password"
                value={loginPassword}
                onChange={(e) => setLoginPassword(e.target.value)}
              />
            </div>
            <button>ログイン</button>
            {/* ↓リンクを追加 */}
            <p>新規登録は<Link to={`/register/`}>こちら</Link></p>
          </form>
        </>
      )}
    </>
  );
};

export default Login;

「react-router-dom」から「Link」をimportして、リンクを追加しています。

「Mypage.js」の完成版は以下です。

/* Mypage.js(完成版) */

import React, { useState, useEffect } from "react";
import { onAuthStateChenged, signOut } from "firebase/auth";
import { auth } from "./FirebaseConfig.js";
import {
  useNavigate,
  Navigate
} from "react-router-dom";

const Mypage = () => {
  const [user, setUser] = useState("");

  const [loading, setLoading] = useState(true);

  useEffect(() => {
    onAuthStateChanged(auth, (currentUser) => {
      setUser(currentUser);
      setLoading(false);
    });
  }, []);

  const navigate = useNavigate();

  const logout = async () => {
    await signOut(auth);
    navigate("/login/");
  }

  return (
    <>
      {!loading && (
        <>
          {!user ? (
            <Navigate to={`/login/`} />
          ) : (
            <>
              <h1>マイページ</h1>
              <p>{user?.email}</p>
              <button onClick={logout}>ログアウト</button>
            </>
          )}
        </>
      )}
    </>
  );
};

export default Mypage;

まとめ

記事の内容をまとめます。

この記事では、Firebaseを使ってReactアプリに「認証機能」を実装する方法を解説しました。
実装したのは以下です。

  • ユーザー登録(マイページにリダイレクト)

  • ログイン(マイページにリダイレクト)

  • マイページでユーザーのメールアドレス表示

  • ログアウト(ログインページにリダイレクト)

ユーザー登録」や「ログイン」を実装する場合、本来なら以下のような状況を想定してバックエンドでの細かな設定が必要です。

  • すでに登録されているユーザー情報である

  • メールアドレスの形式がおかしい

  • パスワードの文字数が少ない

  • など

しかし、Firebaseを使えば細かな設定は不要。
さらに、ユーザーの管理も簡単であるというメリットもあります。

というわけで、記事は以上です。

この記事を共有

以下のボタンを押すとSNSが開きます

Copyright © 2023 RalaCode