지금까지의 설명을 바탕으로 login, createAccount를 완성한다.
그리고 어느page에서든지 사용될 useMe 함수를
hook에 만들어 놓는다.
import { gql, useQuery } from '@apollo/client'
import { MeQuery } from '../graphql/__generated__'
///serve의 me query를 front에서 사용.
export const ME_QUERY = gql`
query me {
me {
id
email
role
}
}
`
///여기까지 작성후 npm run start(generate:codegen)를
///실행시켜야 밑에서 type인<MeQuery>가 생성되어
///사용가능하다.
export const useMe = () => {
///useQuery를 사용해서 ME_QUERY를 실행시키고,
///받아온 data를 return한다.
return useQuery<MeQuery>(ME_QUERY)
}
export const LOCALSTORAGE_TOKEN = 'eat-token'
localstorage에 token을 담기 위해서 key를 하나 만든다.
import { gql, useMutation } from '@apollo/client'
import React from 'react'
import { useForm } from 'react-hook-form'
import { Link } from 'react-router-dom'
import { authTokenVar, isLoggedInVar } from '../apollo'
import Button from '../components/button'
import { FormError } from '../components/form-error'
import { LoginMutation, LoginMutationVariables } from '../graphql/__generated__'
import { LOCALSTORAGE_TOKEN } from '../constant'
interface ILoginForm {
email: string
password: string
}
///login에 사용될 state들의 type을 설정한다.
const LOGIN_MUTATION = gql`
mutation login($loginInput: LoginInput!) {
login(input: $loginInput) {
ok
token
error
}
}
`
///client에서 사용하기 위해서 server에서 login mutation을
///불러들인다. LoginInput!은 server의 dto임.
///이 과정을 거친 후 , 반드시 npm run start(codegen)를
///실행해야 useMutation<>에 들어갈 type이 생성된다.
///생성되는 type은 LoginMutation, LoginMutationVariables
export const Login = () => {
const {
register,
getValues,
formState: { errors, isValid },
handleSubmit,
} = useForm<ILoginForm>({
mode: 'onChange',
})
///react-hook-form
const onCompleted = (data: LoginMutation) => {
const {
login: { ok, token },
} = data
if (ok && token) {
console.log(token)
localStorage.setItem(LOCALSTORAGE_TOKEN, token)
authTokenVar(token)
isLoggedInVar(true)
}
}
///loginMutaion실행 후, 받아온 data를
///data중에 token을 LOCALSTORAGE_TOKEN에 담아주고
///authTokenVar에 담아주고
///isLoggedInVar를 true로 바꿔주는 함수.
///data:LoginMutaion은 바로 밑에서 넣어준 type
const [loginMutation, { data: loginMutationResult, loading }] = useMutation<
LoginMutation,
LoginMutationVariables
>(LOGIN_MUTATION, { onCompleted })
///맨처음에 만든 mutation을 실행시킬
///loginMutation함수임.
const onSubmit = () => {
if (!loading) {
const { email, password } = getValues()
loginMutation({
variables: {
loginInput: {
email,
password,
},
},
})
}
}
///login 버튼을 누르면, form에서 받아온
///password, email을 loginMutation 의
///variables로 보내주고, 실행시킴.
return (
<div className="h-screen flex items-center justify-center flex-col">
<div className="w-full max-w-screen-sm flex flex-col px-5 items-center">
<h3 className="w-full font-medium text-left text-3xl mb-5">
Welcome Back
</h3>
<form
onSubmit={handleSubmit(onSubmit)}
className="grid gap-3 mt-3 mb-5 w-full"
>
<input
{...register('email', {
required: 'Email is required',
maxLength: { value: 30, message: 'max char is 30' },
})}
required
maxLength={30}
type="email"
placeholder="Email"
className="input"
/>
{errors.email?.message && (
<FormError errorMessage={errors.email.message} />
)}
<input
{...register('password', { required: 'Password is required' })}
name="password"
type="password"
placeholder="Password"
className="input"
/>
{errors.password?.message && (
<FormError errorMessage={errors.password?.message} />
)}
<Button loading={loading} actionText="Login" />
{loginMutationResult?.login.error && (
<FormError errorMessage={loginMutationResult?.login.error} />
)}
</form>
<p className="text-center mt-5 text-base">
계정이 없으신가요?
<Link
to="/signup"
className="ml-2 font-semibold text-green-600 hover:underline"
>
///계정이 없으면, create-account page로 이동시킴.
회원가입
</Link>
</p>
</div>
</div>
)
}
import { gql, useMutation } from '@apollo/client'
import React from 'react'
import { useForm } from 'react-hook-form'
import { Link } from 'react-router-dom'
import { useNavigate } from 'react-router-dom'
import Button from '../components/button'
import { FormError } from '../components/form-error'
import {
CreateAccoountMutation,
CreateAccoountMutationVariables,
UserRole,
} from '../graphql/__generated__'
export const CREATE_ACCOUNT = gql`
mutation createAccoount($createAccountInput: CreateAccountInput!) {
createAccount(input: $createAccountInput) {
ok
error
}
}
`
///server에서 createAccount불러들임.
///여기까지 코딩하고 npm run start
interface ICreateAccountForm {
email: string
password: string
role: UserRole
}
///입력받는 state의 type.
export const CreateAccount = () => {
const {
register,
getValues,
formState: { errors, isValid },
handleSubmit,
} = useForm<ICreateAccountForm>({
mode: 'onChange',
defaultValues: { role: UserRole.Client },
})
///react-hook-form.
///role의 default 값은 Client로 한다.
const navigate = useNavigate()
///계정을 만들면, login페이지로 이동시키기 위해
///useNavigate를 사용함.
const onCompleted = (data: CreateAccoountMutation) => {
const {
createAccount: { ok },
} = data
if (ok) {
alert('Account Created. Log in now.')
navigate('/')
///createAccount를 실행하고
///ok를 받으면, alert를 띄우고,'/'페이지로
///redirect한다.
}
}
const [createAccountMutation, { loading, data: createAccountMutaionResult }] =
useMutation<CreateAccoountMutation, CreateAccoountMutationVariables>(
CREATE_ACCOUNT,
{ onCompleted }
)
///getValues로 form의 data를 받아와서
///createAccountMutation의 variables에 넣어서
///실행시킨다.
const onSubmit = () => {
if (!loading) {
const { email, password, role } = getValues()
createAccountMutation({
variables: {
createAccountInput: { email, password, role },
},
})
}
}
return (
<div className="h-screen flex flex-col items-center justify-center ">
<div className="w-full max-w-screen-sm flex flex-col px-5 items-center">
<h4 className="w-full font-medium text-lleft text-3xl mb-5">
Let's get started
</h4>
<form
onSubmit={handleSubmit(onSubmit)}
className="grid gap-3 mt-5 mb-5 w-full"
>
<input
{...register('email', { required: 'Email is required' })}
className="input"
placeholder="email"
type="email"
required
/>
{errors.email?.message && (
<FormError errorMessage={errors.email?.message} />
)}
<input
{...register('password', { required: 'Password is Required' })}
className="input"
placeholder="password"
type="password"
required
/>
{errors.password?.message && (
<FormError errorMessage={errors.password?.message} />
)}
///select부분은 유심히 봐둔다.
<select {...register('role', { required: true })} className="input">
{Object.keys(UserRole).map((role, index) => (
<option key={index}>{role}</option>
))}
</select>
<Button loading={loading} actionText="Create Account" />
********** {createAccountMutaionResult?.createAccount.error && (
<FormError
errorMessage={createAccountMutaionResult?.createAccount.error}
/>
)}*********
///여기서 data:createAccountMutationResult임.
///onCompleted의 (data: CreateAccoountMutation) 와 헷갈리면 안됨.
</form>
<div className="text-lg">
Already have an account?{''}
<Link to="/" className="text-green-400 hover:underline">
Log in now
</Link>
</div>
</div>
</div>
)
}
export default CreateAccount