React, Firebase を使って Googleアカウント使ってのログイン機能をつくってみる
Google アカウント使ったログインに関する処理は一つのモジュール AuthContext.js
にまとめてみました
useContext
使って全体でステータス管理できるようにしてる感じです
実施した環境とざっくり手順はこんなかんじ
- 実施した環境
- Windows11
- node v18.17.1
- React v18.2.0
- firebase v10.7.1
- ざっくり手順
- Firebase ログイン・アプリ利用準備
- React から Firebase 使ったログイン機能作成
あと完成したやつは github においてます(Firebase の定義情報は各自の値で更新が必要)
https://github.com/sueasen/my-app-googleauth/tree/main
Firebase ログイン・アプリ利用準備
- Firebase のサイトにアクセス
https://firebase.google.com/ - [使ってみる] をクリック → Google ログイン(ログイン入力画面は割愛)
- [プロジェクトを作成] をクリック
- プロジェクト情報を以下のように設定しプロジェクト作成 → 準備できたら続行
- [Authentication] をクリック → 始める
- 追加のプロバイダを Google にして以下のように設定して [保存] をクリック
- Firebase をアプリ利用追加の [ウェブアイコン] をクリック
※Firebase SDK の追加にある firebaseConfig はReactから使う情報になります
React から Firebase 使ったログイン機能作成
-
プロジェクトを作成する
こちらを参考に実施(以降は参考にしたやつをベースに進めます)
https://qiita.com/sueasen/items/fe63a1693b2057b29d84 -
Firebase, heroicons をインストール
npm i firebase @heroicons/react
-
src 直下に contexts ディレクトリ作成
ディレクトリ作るの忘れると以降の import でパスがずれます
-
src/contexts 直下に AuthContext.js 作成
firebaseConfig の定義情報 (13行目~) は Firebase のアプリ利用で取得した値を使用
設定する値は Firebase にログインしてプロジェクトの設定から確認できます
import { initializeApp } from 'firebase/app';
import {
getAuth,
GoogleAuthProvider,
signInWithPopup,
signOut,
} from 'firebase/auth';
import { useEffect, useState } from 'react';
import { createContext, useContext } from 'react';
// Context生成(ログインに関する情報を管理)
const AuthContext = createContext();
// firebase の定義情報(各値はFirebaseのアプリ利用で取得した値を使用する)
const firebaseConfig = {
apiKey: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
authDomain: 'xxxxxxxxxx.firebaseapp.com',
projectId: 'xxxxxxxxxx',
storageBucket: 'xxxxxxxxxx.appspot.com',
messagingSenderId: '888888888888',
appId: '1:888888888888:web:xxxxxxxxxxxxxxxxxxxxxxxxx',
};
// firebase, GoogleAuth 初期設定
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const provider = new GoogleAuthProvider();
// AuthContextProvider (Provider)
export const AuthContextProvider = ({ children }) => {
// ログインユーザ
const [loginUser, setLoginUser] = useState();
// 起動時ログイン処理(既にログインしてる場合, ユーザ設定)
useEffect(() => {
// auth 初期化時にログインユーザ設定
auth.onAuthStateChanged((user) => setLoginUser(user));
}, []);
// ログイン処理
const login = async () => {
// Google ログインのポップアップ表示して認証結果取得
const result = await signInWithPopup(auth, provider);
// 認証結果より user 設定
setLoginUser(result.user);
};
// ログアウト処理
const logout = async () => {
await signOut(auth);
setLoginUser(null);
};
// ログイン情報設定したProvider
return (
<AuthContext.Provider
value={{
loginUser,
login,
logout,
}}
>
{children}
</AuthContext.Provider>
);
};
// AuthContextConsumer (useContext) # Provider で囲った範囲で使う必要あり
export const AuthContextConsumer = () => {
return useContext(AuthContext);
};
5.src 直下の App.js を修正
-
AuthContextProvider の import 追加
-
コンポーネント返してるところの全体を AuthContextProvider で括る
App.jsimport { appRouter } from './pages/AppRouter'; import { AuthContextProvider } from './contexts/AuthContext'; import Home from './pages/Home'; import Page1 from './pages/Page1'; import Page2 from './pages/Page2'; import './App.css'; // ページ情報を定義して appRouter に設定 const pages = [ { key: 'Home', path: '/', element: <Home /> }, { key: 'Page1', path: '/page1', element: <Page1 /> }, { key: 'Page2', path: '/page2', element: <Page2 text="text sample" /> }, ]; const router = appRouter(pages); const App = () => { return ( <AuthContextProvider> {router.navbarLink} {/* 位置調整で main で括る */} <main>{router.browserRouter}</main> </AuthContextProvider> ); }; export default App;
6.src/pages 直下に LoginUser.js を作成
import { UserCircleIcon } from '@heroicons/react/24/solid';
import { AuthContextConsumer } from '../contexts/AuthContext';
const LoginUser = () => {
// AuthContextConsumer からログインユーザ、ログイン・ログアウト処理取得
const { loginUser, login, logout } = AuthContextConsumer();
return (
<>
<div className="user_info">
<UserCircleIcon className="user_icon" />
<p className="user_name">
{loginUser ? loginUser.displayName : 'ゲスト'}
</p>
<button className="login_btn" onClick={loginUser ? logout : login}>
{loginUser ? 'logout' : 'login'}
</button>
</div>
</>
);
};
export default LoginUser;
7.src 直下の App.css を修正
-
LoginUser で設定したクラスに対してのスタイルを追記
App.css.user_info { position: fixed; right: 0; top: 0; height: var(--height-navbar); display: flex; align-items: center; color: #ddd; font-size: 14px; letter-spacing: 1px; & .user_icon { height: calc(var(--height-navbar) - 10px); } & .user_name { margin: 0 5px; width: 80px; } & .login_btn { height: calc(var(--height-navbar) - 20px); width: 80px; margin: 0 5px; border-radius: 25px; color: #ddd; background: #1e1d1c; text-transform: uppercase; } & .login_btn:hover { opacity: 0.5; } }
8.src/pages 直下の AppRouter.js を修正
- LoginUser のインポート追加
- links (ヘッダーメニュー部) の最後に LoginUser を追加
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import LoginUser from './LoginUser';
export const appRouter = (pages) => {
const router = () => {
return (
<>
<BrowserRouter basename={process.env.PUBLIC_URL}>
<Routes>
{pages.map((p) => (
<Route key={p.key} path={p.path} element={p.element} />
))}
</Routes>
</BrowserRouter>
</>
);
};
// タグ, クラスなど追加
const links = () => (
<header className="header">
<div className="navtext-container">
<div className="navtext">title</div>
</div>
<input type="checkbox" className="menu-btn" id="menu-btn" />
<label htmlFor="menu-btn" className="menu-icon">
<span className="navicon"></span>
</label>
<ul className="menu">
{pages.map((p) => (
<li key={p.key}>
<a href={`${process.env.PUBLIC_URL}${p.path}`}>{p.key}</a>
</li>
))}
</ul>
<LoginUser />
</header>
);
return {
browserRouter: router(),
navbarLink: links(),
};
};
9.実行確認
起動して [ログイン] クリック → Google認証 → ログイン成功(ユーザ名表示)でOK
※Google認証のポップアップ出てこない時はポップアップブロックを解除