Closed6
Next.jsでFirebase Authenticationを使ったログイン機能を作る
はじめに
自分が作ろうとしているサービスにログイン機能が必要である。
ログイン機能はFirebase Authenticationを使って認証したい。
Next.jsでFirebase Authenticationを使う際に、ステートの管理などややこしかったのでまとめていく。
環境
- Next.js v10.1.3
- Chakra UI
- TypeScript v4.2.3
ステート管理はContext APIとHooksを使う
プロジェクト作成
Firebaseの設定は一からしたいので、TypeScript・Chakra UIの公式サンプルを使用する。
$ npx create-next-app firebase-sample --example with-chakra-ui-typescript
Firebaseの設定
Firebaseのインストール
$ yarn add firebase
Firebaseの初期化
/src/utils/firebase.ts
import 'firebase/auth';
import firebase from 'firebase/app';
const config = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSEGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};
if (!firebase.apps.length) {
firebase.initializeApp(config);
}
export default firebase
/src/utils/AuthUtil.ts
import firebase from './firebase';
import 'firebase/auth';
const provider = new firebase.auth.GoogleAuthProvider();
export function login(): Promise<void> {
return new Promise((resolve, reject) => {
firebase
.auth()
.signInWithRedirect(provider)
.then(() => {
resolve();
})
.catch(error => reject(error));
});
}
export function logout(): Promise<void> {
return new Promise((resolve, reject) => {
firebase
.auth()
.signOut()
.then(() => resolve())
.catch(error => reject(error));
});
}
Context APIを用いてAuthProviderを作る
Context APIについて
コンテクストは、ある React コンポーネントのツリーに対して「グローバル」とみなすことができる、現在の認証済みユーザ・テーマ・優先言語といったデータを共有するために設計されています。
使い方はざっくりと以下の感じ
-
React.createContext('contextName')
でContextを作成する -
<Context.Provider value={data}></Context.Provider>
で値をセットする。 - 使う側は
useContext('contextName')
で2で設定したデータを取得する
AuthContextとProviderを作成する
/src/components/Auth.tsx
import firebase from '../utils/firebase';
import { FC, createContext, useEffect, useState } from 'react';
type AuthContextProps = {
currentUser: firebase.User | null | undefined;
}
const AuthContext = createContext<AuthContextProps>({ currentUser: undefined });
const AuthProvider: FC = ({ children }) => {
const [currentUser, setCurrentUser] = useState<firebase.User | null | undefined>(undefined);
useEffect(() => {
firebase.auth().onAuthStateChanged((user) => {
// ログイン状態が変化すると呼ばれる
setCurrentUser(user);
})
}, []);
return (
<AuthContext.Provider value={{ currentUser: currentUser }}>
{children}
</AuthContext.Provider>
);
}
export { AuthContext, AuthProvider }
/src/pages/_app.tsx
import { ChakraProvider } from '@chakra-ui/react'
import theme from '../theme'
import { AppProps } from 'next/app'
import { AuthProvider } from '../components/Auth'
function MyApp({ Component, pageProps }: AppProps) {
return (
+ <AuthProvider>
<ChakraProvider resetCSS theme={theme}>
<Component {...pageProps} />
</ChakraProvider>
+ </AuthProvider>
)
}
export default MyApp
動かしてみる
ログイン・ログアウトボタンを作る
/src/components/Button.tsx
import { Button } from '@chakra-ui/react'
import { login, logout } from '../utils/AuthUtil'
type BaseButtonProps = {
text: string;
onclick(): void;
}
const BaseButton: React.FC<BaseButtonProps> = (props) => (
<Button
backgroundColor="#48BB78"
color="#fff"
width="100px"
height={{ base: '35px', sm: '35px', md: '35px', lg: '35px' }}
marginRight={2}
onClick={props.onclick}
>{props.text}
</Button>
)
const LoginButton = () => (
<BaseButton onclick={() => login()} text="LOGIN"/>
);
const LogoutButton = () => (
<BaseButton onclick={() => logout()} text="LOGOUT"/>
);
export { BaseButton, LoginButton, LogoutButton }
/src/pages/_app.tsx
import { Center } from '@chakra-ui/react'
import { Hero } from '../components/Hero'
import { Container } from '../components/Container'
import { Main } from '../components/Main'
import { DarkModeSwitch } from '../components/DarkModeSwitch'
import { useContext } from 'react'
import { AuthContext } from '../components/Auth'
import { LoginButton, LogoutButton } from '../components/Button'
const Index = () => {
const { currentUser } = useContext(AuthContext);
return (
<Container height="100vh">
<Hero />
<Main>
<Center>
{currentUser
? <LogoutButton />
: <LoginButton />
}
</Center>
</Main>
<DarkModeSwitch />
</Container>
)
}
export default Index
未ログイン時はLOGINボタンが表示される。
ログイン時はLOGOUTボタンが表示される。
このスクラップは2021/04/25にクローズされました