バージョン
- react:18.0
- next.js:13.5.6
- typescript:5.0
前準備
- VSコードから使用するディレクトリに移動
- コマンド:「npx create-next-app@latest」 で作成
- プロジェクトネームを作成し、import alias? のみNoで、他はYesにして進む
- 作成したプロジェクトに移動
- コマンド:「code.」 で立ち上げ
- コマンド:「npm run dev」 でローカル環境が起動
ソースファイルの前準備
- app/layout の lang を ja に変更しておく
- app/globals.css の @tailwind 以外は削除しておく
Firebaseのセットアップ(初期化)
- プロジェクトの追加をクリック
- アナリティクスは無効で次へ
アナリティクス:ユーザーがどれくらい訪れたか?等が分かる機能 - 続行
- をクリックして次へ
- ネーム入力、Hostingは一旦空で次へ
Firebase Hosting はデプロイで使う機能 - 何も変更せず、コンソールに進むをクリック
-
プロジェクトの設定をクリック
- 「npm install firebase」をコピーしてVSCodeに戻りコマンドを叩く
- 次に Firebase を初期化し、使用するプロダクトの SDK の利用を開始します。の下記ソースコードをコピーしておく
- src\app 直下に「firebase.ts」ファイルを作成
- コピーしたソースを張り付ける
※apiKey や authDomain は 見られたらいけないので、git管理するならenvファイルや .gitignore を使うこと - firebase.ts ファイルに書き加えて、初期化完了
import { initializeApp } from "firebase/app";import { getAuth } from "firebase/auth";import { getFirestore } from "firebase/firestore";
const firebaseConfig = {apiKey: "AIzaSyALIAt79-kG5Z0CzpBLc_KSVexcHOK-HGU",authDomain: "chatapplication-with-cha-ac9a9.firebaseapp.com",projectId: "chatapplication-with-cha-ac9a9",storageBucket: "chatapplication-with-cha-ac9a9.appspot.com",messagingSenderId: "745628687411",appId: "1:745628687411:web:1b2274a27ea9f17c4dd746",};
const app = initializeApp(firebaseConfig);export const db = getFirestore(app);export const auth = getAuth(app);
新規登録ページの作成
CSS記法はTaillwind
import React from "react";
const Register = () => {
return (
<>
<form className="bg-white p-8 rounded-lg shadow-md w-96">
<h1 className="mb-4 text-2xl text-gray-700 font-medium font-">
新規登録
</h1>
<div className="mb-4">
<label className="block text-sm font-medium">メールアドレス</label>
<input
type="text"
className="mt-1 border-2 rounded-md w-full p-2"
></input>
</div>
<div className="mb-4">
<label className="block text-sm font-medium">パスワード</label>
<input
type="password"
className="mt-1 border-2 rouded-mt w-full p-2"
></input>
</div>
<button
type="submit"
className="bg-blue-500 hover:bg-blue-700 text-white font-bold px-4 py-2 rounded-md"
>
新規登録
</button>
</div>
<div>
<span>既にアカウントをお持ちの方:</span>
<button className="text-blue-500 hover:text-blue-800 font-bold">
ログインページへ
</button>
</div>
</form>
</div>
</>
);
};
export default Register;
新規登録機能
打ち込んだメールアドレスのバリデーションチェック
- https://react-hook-form.com/get-started からnpmをインストール
"use client";
import React from "react";
import { useForm, SubmitHandler } from "react-hook-form";
type Inputs = {
email: string;
password: string;
};
const Register = () => {
const {
// react側が用意しているプロパティ
register,
handleSubmit,
formState: { errors },
} = useForm<Inputs>();
const onSubmit: SubmitHandler<Inputs> = async (data) => {
console.log(data);
};
return (
<>
<form
onSubmit={handleSubmit(onSubmit)}
className="bg-white p-8 rounded-lg shadow-md w-96"
>
<h1 className="mb-4 text-2xl text-gray-700 font-medium font-">
新規登録
</h1>
<div className="mb-4">
<label className="block text-sm font-medium">メールアドレス</label>
<input
// requiredの値がエラーとして表示される
{...register("email", { required: "メールアドレスは必須です" })}
type="text"
className="mt-1 border-2 rounded-md w-full p-2"
/>
{/* emailのバリデーションエラー */}
{errors.email && (
<span className="text-red-500 text-sm">
{errors.email?.message}
</span>
)}
</div>
~省略~
正規表現のバリデーションチェック
- メールアドレスの正規表現 | 正規表現入門 から正規表現をコピペ
- /^[a-zA-Z0-9_.+-]+@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$/
- minLength():文字数制御
<div className="mb-4">
<label className="block text-sm font-medium">メールアドレス</label>
<input
// requiredの値がエラーとして表示される
{...register("email", {
required: "メールアドレスは必須です",
// 正規表現
pattern: {
/^[a-zA-Z0-9_.+-]+@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$/,
message: "正しい形式で入力してください",
},
})}
type="text"
className="mt-1 border-2 rounded-md w-full p-2"
/>
{/* emailのバリデーションエラー */}
{errors.email && (
<span className="text-red-500 text-sm">
{errors.email?.message}
</span>
)}
</div>
<div className="mb-4">
<label className="block text-sm font-medium">パスワード</label>
<input
{...register("password", {
required: "パスワードは必須です",
// 文字数制御
minLength: {
message: "6文字以上で入力してください。",
},
})}
type="password"
className="mt-1 border-2 rouded-mt w-full p-2"
/>
</div>
{errors.password && (
<span className="text-red-500 text-sm">
{errors.password?.message}
</span>
)}
Firebaseにユーザー登録
-
ユーザー管理の為firebaseからをクリック
- メール&パスワードを有効にして保存
-
ウェブサイトで Firebase Authentication を使ってみるの新しいユーザーを登録するを参考に書く
import { auth } from "@/app/firebase";
import { createUserWithEmailAndPassword } from "firebase/auth";
import { useForm, SubmitHandler } from "react-hook-form";
type Inputs = {
email: string;
password: string;
};
const Register = () => {
const onSubmit: SubmitHandler<Inputs> = async (data) => {
await createUserWithEmailAndPassword(auth, data.email, data.password)
.then((userCredential) => {
const user = userCredential.user;
})
.catch((error) => {
alert(error);
});
};
useRouterを使いログインページに遷移
- pushに飛ばしたいパスを指定
- 今回だと新規登録を押したらログインページに飛ぶ
import { useRouter } from "next/navigation";
const router = useRouter();
router.push("auth/login")
ログインページ作成
- 新規登録ページをコピペして部分的に変更
"use client";
import { auth } from "@/app/firebase";
import { error } from "console";
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
} from "firebase/auth";
import { useRouter } from "next/navigation";
import React from "react";
import { useForm, SubmitHandler } from "react-hook-form";
type Inputs = {
email: string;
password: string;
};
const Register = () => {
const router = useRouter();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<Inputs>();
const onSubmit: SubmitHandler<Inputs> = async (data) => {
await signInWithEmailAndPassword(auth, data.email, data.password)
.then((userCredential) => {
const user = userCredential.user;
router.push("/");
})
.catch((error) => {
if (error.code === "auth/invalid-credential") {
alert("存在しないユーザーです。");
} else {
alert(error.message);
}
});
};
return (
<>
<form
onSubmit={handleSubmit(onSubmit)}
className="bg-white p-8 rounded-lg shadow-md w-96"
>
<h1 className="mb-4 text-2xl text-gray-700 font-medium font-">
ログイン
</h1>
<div className="mb-4">
<label className="block text-sm font-medium">メールアドレス</label>
<input
// requiredの値がエラーとして表示される
{...register("email", {
required: "メールアドレスは必須です",
// 正規表現
pattern: {
/^[a-zA-Z0-9_.+-]+@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$/,
message: "正しい形式で入力してください",
},
})}
type="text"
className="mt-1 border-2 rounded-md w-full p-2"
/>
{/* emailのバリデーションエラー */}
{errors.email && (
<span className="text-red-500 text-sm">
{errors.email?.message}
</span>
)}
</div>
<div className="mb-4">
<label className="block text-sm font-medium">パスワード</label>
<input
{...register("password", {
required: "パスワードは必須です",
// 文字数制御
minLength: {
message: "6文字以上で入力してください。",
},
})}
type="password"
className="mt-1 border-2 rouded-mt w-full p-2"
/>
</div>
{errors.password && (
<span className="text-red-500 text-sm">
{errors.password?.message}
</span>
)}
<button
type="submit"
className="bg-blue-500 hover:bg-blue-700 text-white font-bold px-4 py-2 rounded-md"
>
ログイン
</button>
</div>
<div>
<span>アカウントをお持ちでない方:</span>
<Link
href={"/auth/register"}
className="text-blue-500 hover:text-blue-800 font-bold"
>
新規登録へ
</Link>
</div>
</form>
</div>
</>
);
};
export default Register;