問題となっているuseAuthフック
結論
useAuthは「認証ドメイン全部」を抱えすぎていて、本来は分割すべき状態
import { useEffect } from 'react'
import axios from 'axios'
import { useRouter } from 'next/router'
import { setCookie, destroyCookie, parseCookies } from 'nookies'
import useRequest, { fetcher } from './useRequest'
import type { AxiosError, AxiosResponse } from 'axios'
import { AUTH_TOKEN_KEY } from '@/constant'
import { APIRequest, HTTPMethod } from '@/lib/api/APIRequest'
import { User, UserAndToken, UserDetail, UserExperience, UserPreference } from '@/models'
interface Referral {
redirect?: string
}
export default function useAuth({ redirectTo = '', redirectIfFound = false } = {}) {
const {
data: user,
mutate,
error,
} = useRequest<User>({ url: '/v1/me' } as APIRequest, {
onError: (error: any) => {
if (error.response?.status != 401) return
handleAuthError()
},
})
const router = useRouter()
const loadingUser = user === undefined && !error
useEffect(() => {
if (loadingUser) return
if ((redirectTo && !redirectIfFound && !user) || (redirectIfFound && user)) {
router.push(redirectTo)
}
}, [user, redirectIfFound, redirectTo, loadingUser, router])
const mutateUserDetail = (detail: UserDetail): Promise<User> => mutate({ ...user, detail })
const mutateUserPreference = (preference: UserPreference): Promise<User> =>
mutate({ ...user, preference })
const mutateUserExperience = (experience: UserExperience): Promise<User> =>
mutate({ ...user, experience })
const setToken = (token: string) =>
setCookie(null, AUTH_TOKEN_KEY, token, {
maxAge: 30 * 24 * 60 * 60,
path: '/',
})
const login = async (email, password, rememberMe = false): Promise<User> => {
const response = await fetcher<UserAndToken>({
url: '/v1/login',
method: HTTPMethod.POST,
params: { email, password },
} as APIRequest)
setToken(response.token)
localStorage.setItem('rememberMe', rememberMe ? '1' : '0')
mutate(response.user, { rollbackOnError: true })
return response.user
}
const refresh = async (): Promise<void> => {
try {
const response = await fetcher<UserAndToken>({
url: `/v1/refresh`,
method: HTTPMethod.POST,
})
setToken(response.token)
const user: User = await fetcher<User>({
url: '/v1/me',
method: HTTPMethod.GET,
})
mutate(user, { rollbackOnError: true })
} catch (e) {
console.error(e)
logout(false)
}
}
const register = async (email, password, passwordConfirmation): Promise<User> => {
const registerVia = router.asPath
const response = await fetcher<UserAndToken>({
url: '/v1/register',
method: HTTPMethod.POST,
params: { email, password, passwordConfirmation, registerVia },
} as APIRequest)
setToken(response.token)
mutate(response.user, { rollbackOnError: true })
return response.user
}
const logout = async (needRequest = true) => {
if (location.href.includes('login')) return
if (location.href.includes('register')) return
if (location.href.includes('apply/step1')) return
if (location.href.includes('landing')) return
const cookies = parseCookies()
if (!cookies[AUTH_TOKEN_KEY]) return
needRequest &&
(await fetcher<User>({
url: '/v1/logout',
method: HTTPMethod.DELETE,
} as APIRequest))
destroyCookie(null, AUTH_TOKEN_KEY)
mutate(null)
localStorage.removeItem('rememberMe')
}
const leave = async () => {
await fetcher({
url: '/v1/user/leave',
method: HTTPMethod.DELETE,
} as APIRequest)
destroyCookie(null, AUTH_TOKEN_KEY)
mutate(null)
localStorage.removeItem('rememberMe')
}
const forgotPassword = async (email): Promise<void> => {
const response = await fetcher<User>({
url: '/v1/forgot-password',
method: HTTPMethod.POST,
params: { email },
} as APIRequest)
}
const resetPassword = async (email, password, passwordConfirmation): Promise<void> => {
const token = router.query.token
const response = await fetcher<UserAndToken>({
url: `/v1/reset-password/${token}`,
method: HTTPMethod.POST,
params: { token, email, password, passwordConfirmation },
} as APIRequest)
}
const setReferral = async (referral: string): Promise<string | null> => {
if (!!user) {
return null
}
const respnse = await fetcher<Referral>({
url: `/v1/set-referral/${referral}`,
method: HTTPMethod.POST,
} as APIRequest)
return respnse.redirect ?? '/'
}
const handleAuthError = async () => {
const rememberMe = JSON.parse(localStorage.getItem('rememberMe') ?? '0')
!!rememberMe && (await refresh())
!rememberMe && (await logout(false))
}
const onResponse = (response: AxiosResponse): AxiosResponse => response
const onResponseError = (error: AxiosError): Promise<AxiosError> | undefined => {
if (error.response?.status == 401 && error.config.method !== 'get') {
handleAuthError()
return
}
return Promise.reject(error)
}
axios.interceptors.response.use(onResponse, onResponseError)
return {
loadingUser,
user,
mutateUserDetail,
mutateUserPreference,
mutateUserExperience,
login,
register,
logout,
leave,
forgotPassword,
resetPassword,
setReferral,
}
}