どうも、シローです。
アプリケーションではユーザの認証機能が必要になることが多いです。
認証にはサーバサイドとフロントサイドのそれぞれの担当があり、主にフロントサイドでは
認証済みのユーザを取得したり、
ユーザが取得できたかどうかで、ページへのアクセスを制限したり、表示を切り替える制御を行う
といったことが必要になります。
そこで今回はユーザ情報の取得や状態の管理方法としてcustom hooksを用いた例を紹介します。
custom hooksとは
Next.jsではコンポーネント内で
動的に変化する値の格納と変更を可能にするuseStateや
ある値が変更されたら特定の処理を行うuseEffectなどを用いることで
ユーザの操作やアプリケーションの状態に応じた画面を描画することが可能になります。
ただ、一つのコンポーネントには
DOMを出力する部分と
描画に用いるデータの管理やサーバとの通信といったロジック部分
が混在していることが多く、ソースコードを長くして可読性が悪くなるのに繋がります。
そこで便利になるのが、ロジック部分を別のファイルに切り出すことを可能にするcustom hooks(https://reactjs.org/docs/hooks-custom.html)です。
ユーザ取得部分をcustom hooksで実装
実際にユーザの取得部分をcustom hooksで実装した例を載せます。
全ページでユーザがログインしているかどうかを確認する必要があると思ったので、レイアウトのコンポーネントでcustom hooksを使うようにしています。
レイアウト部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
import Button from 'components/atoms/Button' import UserMenuBar from 'components/organizations/commons/UserMenuBar' import { useRouter } from 'next/router' import { useApi as useGetMe } from 'api/userGetMe' type Props = { hideLogin?: boolean hideRegist?: boolean children: JSX.Element } const SingleLayout: React.FC<Props> = (props) => { const { user, loading } = useGetMe() // <= ここがcustom hooks使用部分 const router = useRouter() const onClickTop = () => router.push('/') const onClickLogin = () => router.push('/login') const onClickRegist = () => router.push('/regist') const LoginRegistButtons = () => ( <div className="flex"> { props.hideLogin ? null : <Button text="ログイン" mode="second" className={ !props.hideRegist ? "mr-4" : ""} onClick={onClickLogin} /> } { props.hideRegist ? null : <Button text="会員登録" onClick={onClickRegist} /> } </div> ) return ( <div> <header className="h-16 bg-gray-800 p-2 flex justify-between items-center"> <h1 className="cursor-pointer text-3xl text-slate-50" onClick={onClickTop} >NetaVerse</h1> { loading ? null : <div> { user ? <UserMenuBar user={user} /> : <LoginRegistButtons /> } </div> } </header> <main> {props.children} </main> </div> ) } SingleLayout.defaultProps = { hideLogin: false, hideRegist: false } export default SingleLayout |
useGetMe
がcustom hooksを使用している部分になります。
user
にログイン済みユーザ情報、loading
にユーザ取得のAPIを実行中かどうかの状態を保持しています。
custom hooksを呼び出している時点で、hook内部ではユーザ情報の取得が実行されているので、コンポーネント内でAPIの実行を叩く必要はありません。
custom hooks
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
import axios from "utils/axios" import { User } from "models/user" import {useState, useEffect} from "react" export type Response = { data: User } export const api = async () => { try { const { data }: { data: Response } = await axios.get('/api/user/me') return data } catch (e) { throw e } } export const useApi = () => { const [user, setUser] = useState<User|null>(null) const [error, setError] = useState<string | null>(null) const [loading, setLoading] = useState<boolean>(true) useEffect(() => { ( async () => { try { const { data } = await api() console.log(data) setUser(data) } catch (e) { setError(e) } finally { setLoading(false) } } )() },[]) return { user, error, loading } } |
useApiの内部ではuseStateでユーザ情報やAPIの実行済みかを保持しており、useEffectで初回のみAPIを実行してuseStateのそれらの値を更新しています。
このcustom hooksを利用しているコンポーネント側では、APIの実行後にユーザ情報がuser
に格納されていることになります。