目指すところ
こんにちは
FirebaseAuthenticationをReactプロジェクトに実装を試みました
◆目指すところ
2つ認証方法でサインアップ(メール、Google)
◆本記事のオチ
メール認証…OK、Google認証…YET
タイトルの失敗談とは、取り組んでる間にエラーに見舞われ本目的(メールとGoogle認証)に夢半ばに挫けた結果となった為です
※FirebaseAuthenticationについては割愛
こういった経緯を綴ります
環境
Windows10
Firebase 9.15
React 18.2
Node.js 18.12.1
作業内容
基本参考にするサイト
まずはじめにこちらの2つのサイトを基本として手順をすすめた
このサイトの手順に従ってコードを書く
記事ごとコードが散らばってますが差分に注目していただければ幸いです
これから、コケたところをかいつまんで書きます
ディレクトリ構成
構成はRalacodeさんに準拠
[src]
├─[components]
| ├ Home.js
| ├ Login.js
| ├ Register.js
| ├ PrivateRoute.js
| └ PublicRoute.js
├─[context]
| └ AuthContext.js
├─App.js
├─App.css
├─firebase.js
└─Page404.js
まず、メール認証
元のコード
import { useState } from 'react';
import { createUserWithEmailAndPassword } from "firebase/auth";
const SignUp = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (event) => {
event.preventDefault();
const { email, password } = event.target.elements;
auth.createUserWithEmailAndPassword(email.value, password.value); };
上のサンプルコードをそのまま適用し、適当なメールアドレス(xxx@xxx.xxx)でFirebaseにサインアップを試みたが下記のエラーが返ってきた
Uncaught (in promise) TypeError: can't assign to property "_canInitEmulator" on "xxx@xxx.xxx"
こちらのサイトを参考にする
auth.createUserWithEmailAndPassword と書くとエラーが吐かれたと推測し、
createUserWithEmailAndPassword(auth, ...) に修正
import { auth } from '../firebase'; も追加すると正常に動作
修正コード
import { useState } from 'react';
import { auth } from '../firebase';
import { createUserWithEmailAndPassword } from "firebase/auth";
const SignUp = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = (event) => {
event.preventDefault();
const { email, password } = event.target.elements;
createUserWithEmailAndPassword(auth, email.value, password.value)
};
React Routerエラー
そして、参照サイトのコードをコピペで進んでいた私に待ち構えていたのは、ReactRouterのエラーでした
元のコード
import SignUp from './components/SignUp';
import { AuthProvider } from './context/AuthContext';
import { BrowserRouter, Route } from 'react-router-dom';
function App() {
return (
<AuthProvider>
<div style={{ margin: '2em' }}>
<BrowserRouter>
<Route path="/signup" component={SignUp} />
</BrowserRouter>
</div>
</AuthProvider>
);
}
export default App;
このあと出たエラーコード
<Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your Route in a Routes.
包めといわれても…
エラーコンソールをよく見ると
Please wrap your Route in a Routes.
からのー…あり?
エラーコードをググって以下のサイトにたどり着き修正していく
(変更前)
<BrowserRouter>
<Route path="/signup" conponent={SignUp} />
</BrowserRouter>
(変更後)
import { BrowserRouter, Route, Routes } from 'react-router-dom';
…略…
<BrowserRouter>
<Routes>
<Route path="/signup" element={<SignUp />} />
</Routes>
</BrowserRouter>
なるほど、RouteをRoutesで囲めということを理解
importにRoutesを追加
Routeの親にRoutesを追加(=Routesの子にRoute)
Routeの中、conponentのままではまだエラーが返ってきたので
elementを試してみる(componentからelementに変更)と正常に動作した
再度メールでFirebaseにサインインを試すとメール認証経由で、Firebase Authenticationにユーザー新規登録されていることも確認した(よし)
次に、アクセス制限設定
メール認証がクリア出来たのでアクセス制限設定に取り掛かる
また次のサイトを参考にしてとりあえずコピペで進めた
使われてないコマンドでコケる
その名の通り、Reactバージョンアップに伴いコマンド名が大々的に変わったようです
元のコード
import { auth } from '../firebase';
import { useHistory } from 'react-router-dom';
const Home = () => {
const history = useHistory();
const handleLogout = () => {
auth.signOut();
history.push('/login');
};
return (
<div>
<h1>ホームページ</h1>
<button onClick={handleLogout}>ログアウト</button>
</div>
);
};
export default Home;
そして、コンソールに次のエラーが返ってきた
export 'useHistory' (imported as 'useHistory') was not found in 'react-router-dom'
In react-router-dom v6 useHistory() is replaced by useNavigate().
これか。
修正コード
import { auth } from '../firebase';
import { useNavigate } from 'react-router-dom';
const Home = () => {
const navigate = useNavigate();
const handleLogout = () => {
auth.signOut();
navigate('/login');
};
return (
<div>
<h1>ホームページ</h1>
<button onClick={handleLogout}>ログアウト</button>
</div>
);
};
export default Home;
useHistoryをuseNavigateに置き換え、関係するところも修正、pushは消した
(Reactのバージョンによってコマンド表記・役割が変わることが判明。厄介だな。今後注意しよう)
この記事も参考にした
書き換えるとエラーは表示されなくなった
次に進む
元のコード+上記の修正分
import { auth } from '../firebase';
import { useNavigate, Redirect } from 'react-router-dom';
import { useAuthContext } from '../context/AuthContext';
const Home = () => {
const navigate = useNavigate();
const { user } = useAuthContext();
const handleLogout = () => {
auth.signOut();
navigate('/login');
};
if (!user) {
return <Redirect to="/login" />;
} else {
return (
<div>
<h1>ホームページ</h1>
<button onClick={handleLogout}>ログアウト</button>
</div>
);
}
};
export default Home;
はい、出ましたエラーはこちら
export 'Redirect' (imported as 'Redirect') was not found in 'react-router-dom'
またこれか(Reactのバージョンごとのコマンド違い問題)
RedirectはNavigateに統合されたことを知り修正
修正コード
import { auth } from '../firebase';
import { useNavigate, Navigate } from 'react-router-dom';
import { useAuthContext } from '../context/AuthContext';
const Home = () => {
const navigate = useNavigate();
const { user } = useAuthContext();
const handleLogout = () => {
auth.signOut();
navigate('/login');
};
if (!user) {
return <Navigate to="/login" />;
} else {
return (
<div>
<h1>ホームページ</h1>
<button onClick={handleLogout}>ログアウト</button>
</div>
);
}
};
export default Home;
PrivateRouteを設定
手順の通りコピペでファイルを作った
import { Route, Redirect } from 'react-router-dom';
import { useAuthContext } from '../context/AuthContext';
const PrivateRoute = ({ component: Component, ...rest }) => {
const { user } = useAuthContext();
return (
<Route
{...rest}
render={(routeProps) => {
return user ? <Component {...routeProps} /> : <Redirect to="/login" />;
}}
/>
);
};
export default PrivateRoute;
上記で出てきた話。RedirectをNavigateに書き換え(修正コードは省略)
App.jsファイルでRouteコンポーネントを作成したPrivateRouteに変更します。
import Home from './components/Home';
import SignUp from './components/SignUp';
import Login from './components/Login';
import { AuthProvider } from './context/AuthContext';
import { BrowserRouter, Route } from 'react-router-dom';
import PrivateRoute from './components/PrivateRoute';
function App() {
return (
<AuthProvider>
<div style={{ margin: '2em' }}>
<BrowserRouter>
<PrivateRoute exact path="/" component={Home} />
<Route path="/signup" component={SignUp} />
<Route path="/login" component={Login} />
</BrowserRouter>
</div>
</AuthProvider>
);
}
export default App;
そうしてエラーコンソールには…
Uncaught Error: [PrivateRoute] is not a component. All component children of must be a or
エラーメッセージでいくつかググったがまだこのエラーは解決していない
ここで脱線します
ReactRouterエラーで試行錯誤していた矢先、突然、プロジェクトのゴキゲンが悪くなった。どうやらタイミング的にこの2つのコマンドを同時に実行してからだ(それが原因かは不明)
>npm start
>npm audit fix --force
localhostにサーブしてみて得られたエラーコードでぐぐって試してみたがなんとも直らず…
(エラーコードと参考サイトは省略)
なんだかんだでたどり着いたnode moduleがあやしい…これで直ってくれと天に祈る
直らなかった…
>npm start
確認にもう一度localhostにサーブしてみる。
→やはりエラー
こちらがエラー画面(画像)
>npm audit --force
エラーコードではなくエラーの中身に注目
パッケージが壊れてるっぽいので一旦削除してリビルドしてみる。
>npm r "エラーで表示された開発が終了されていたパッケージとか"
そうしてリビルドすること…
npm start を試してみる
>npm start
無事にlocalhostにサーブできました。
戻ってきた!
>npm run build
こちらも問題なく動いたので一安心です
さてここで大問題…
FirebaseAuthentication、Googleでのログイン実装がまだできてない
悲しい…
とりあえず本記事はここで筆を置きます
ここまで読んでいただきありがとうございました
後編に続く…!?
続きます…!!!!
FirebaseAuthentication公式ドキュメントの紹介
やっぱり公式ドキュメント最強説、ということで少し読んだ
反省+今後の課題
- FirebaseAuthentication、Google認証の実装はできなかった
- Node.jsの仕組みを理解する
- Docker環境でReact+Firebase構築を学ぶ(将来用に)
- FirebaseAuthentication、Google認証の実装する(次回投稿予定)