->naver지도만들기
들어가기
enter Form을 만들어본다
react-hook-form을 이용해서
Input component에 react-hook-form을 적용시켜 보자~
interface InputProps {
label: string
name: string
kind?: 'text' | 'phone' | 'price'
[key: string]: any ///이게 아무 Props를 보낼 수 있게 해줌.
///밑에 ...rest나 register를 여기에 등록하는거 없이
///적용되게 함.
}
export default function Input({
label,
name,
kind = 'text',
register,
...rest
}: InputProps) {
return (
<div>
<label
className="mb-1 block text-sm font-medium text-gray-700"
htmlFor={name} ///Props로 받는 name
>
{label} ///Props로 받는 name
</label>
{kind === 'text' ? (
<div className="rounded-md relative flex items-center shadow-lg">
<input
id={name}
{...register}
{...rest} ///input으로 name, ...register, ...rest를 받음.
///...register와 ...rest를 통해 enter.tsx에서
///사용하는 react-hook-form을 여기서 사용 가능해짐.
className="appearance-none w-full px-3 py-2 border border-gray-300 rounded-md shadow-lg placeholder-gray-400 focus:outline-none focus:ring-orange-500 focus:border-orange-500"
/>
</div>
) : null}
{kind === 'price' ? (
<div className="rounded-md relative flex items-center shadow-lg">
<div className="absolute left-0 pointer-events-none pl-3 flex items-center justify-center">
<span className="text-gray-500 absolute left-1 text-sm">$</span>
</div>
<input
id={name} ///props로 받는 name
{...rest}
{...register} ///input으로 name, ...register, ...rest를 받음.
///...register와 ...rest를 통해 enter.tsx에서
///사용하는 react-hook-form을 여기서 사용 가능해짐.
className="appearance-none w-full px-3 py-2 border border-gray-300 rounded-md shadow-lg placeholder-gray-400 focus:outline-none focus:ring-orange-500 focus:border-orange-500"
/>
<div className="absolute right-0 pointer-events-none pr-3 flex items-center">
<span className="text-gray-500">KRW</span>
</div>
</div>
) : null}
{kind === 'phone' ? (
<div className="flex rounded-md shadow-lg">
<span className="flex items-center justify-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text0gray-500 select-none text-sm">
+82
</span>
<input
id={name}
{...register}
{...rest}
className="appearance-none w-full px-3 py-2 border border-gray-300 rounded-md shadow-lg placeholder-gray-400 focus:outline-none focus:ring-orange-500 focus:border-orange-500"
/>
</div>
) : null}
</div>
)
}
집중해서 봐야 될것이 많지만, 여기서는 react-hook-form만 다루도록 한다.
import { useEffect, useState } from 'react'
import type { NextPage } from 'next'
import Input from '../components/input'
import Button from '../components/button'
import { useForm } from 'react-hook-form'
import useMutation from '../libs/client/useMutation'
import { useRouter } from 'next/router'
function cls(...classnames: string[]) {
return classnames.join('')
}
interface EnterForm {
email?: string
phone?: string
}
///EnterForm에 사용될 argument
interface TokenForm {
token: string
}
///TokenForm에 사용될 argument
interface MutationResult {
ok: boolean
}
///MutationResult에 사용될 argumuent
export function Enter() {
const [enter, { loading, data, error }] =
useMutation<MutationResult>('/api/users/enter')
const [
confirmToken,
{ loading: tokenLoading, data: tokenData, error: tokenError },
] = useMutation<MutationResult>('/api/users/confirm')
const { register, reset, handleSubmit } = useForm<EnterForm>()
///enter페이지는 두개의 Form으로 이루어짐,
const { register: tokenRegister, handleSubmit: tokenHandleSubmit } =
useForm<TokenForm>()
///enter페이지는 두개의 form으로 이루어짐, 이름 중복을 피하기 위해서~
const [method, setMethod] = useState<'email' | 'phone'>('email')
const onEmailClick = () => {
reset(), setMethod('email') ///Enter form의 reset이용!
}
const onPhoneClick = () => {
reset(), setMethod('phone') ///Enter form의 reset이용
}
const onValid = (validForm: EnterForm) => {
if (loading) return
enter(validForm)
}
///Enter form을 submit했을때, onValid함수.
///enter은 useMutation 다음에 다룰예정
console.log(data)
const onTokenValid = (validForm: TokenForm) => {
confirmToken(validForm)
}
///Token form을 submit했을때, onValid함수.
///conformToken은 useMutation 다음에 다룰예정
const router = useRouter()
useEffect(() => {
if (tokenData?.ok) {
router.push('/')
}
}, [tokenData, router])
///token을 입력하고 나면, 메인page(index.tsx)로 이동시킴.
///router가 Next꺼임을 체크할것(react꺼 아님.)
return (
<div className="mt-16 px-4">
<h1>{data?.payload}</h1>
<h3 className="text-3xl font-bold text-center">Enter to Apple Market</h3>
<div className="mt-16">
{data?.ok ? (
<form
onSubmit={tokenHandleSubmit(onTokenValid)} ///form에 onSubmit장착
///tokenHandleSubmit장착
className="flex flex-col mt-8 space-y-4 mx-5"
>
<Input
register={tokenRegister('token', {
required: true,
minLength: 5,
})}
name="token"
label="Confirm Token"
type="number"
required
/> ///Input Component에 보내어지는 props들
///register가 보내지는 거에 유의한다.
///[ket:string]:any, {...rest}로 명시하지 않은 props받는다는거 유의!
<Button text={tokenLoading ? 'loaindg' : 'Confirm Token'} />
///useForm에서 tokenLoading이면, loading
</form>
) : (
<>
{' '}
<div className="flex flex-col items-center">
<h5 className="text-sm text-gray-500 font-medium">
Enter using:
</h5>
<div className="grid grid-cols-2 gap-10 mt-8 border-b w-full pb-2">
<button
className={cls(
'pb-4 font-medium border-b-2',
method === 'email'
? ' border-red-500 text-red-500'
: ' border-transparent hover:text-gray-400 '
)}
onClick={onEmailClick}
>
Email
</button>
<button
className={cls(
'pb-4 border-b-2 font-medium',
method === 'phone'
? ' border-red-500 text-red-500'
: ' border-transparent hover:text-gray-400 '
)}
onClick={onPhoneClick}
>
Phone
</button>
</div>
</div>
<form ///useForm<EnterForm>의 react-hook-form
onSubmit={handleSubmit(onValid)} ///onSubmit, handleSubmit
className="flex flex-col mt-8 space-y-4 mx-5"
>
{method === 'email' ? (
<Input
register={register('email', { required: true, minLength: 5 })}
name="email"
label="Email address"
type="email"
required
/>//////////////////////Input Component에 보내어지는 props들
///register가 보내지는 거에 유의한다.
///[ket:string]:any, {...rest}로 명시하지 않 은 props받는다는거 유의!
) : null}
{method === 'phone' ? (
<Input
register={register('phone', { required: true, minLength: 5 })}
name="phone"
label="phone number"
type="number"
kind="phone"
required
/>//////////////////////Input Component에 보내어지는 props들
///register가 보내지는 거에 유의한다.
///[ket:string]:any, {...rest}로 명시하지 않 은 props받는다는거 유의!
) : null}
{method === 'email' ? (
<Button text={loading ? 'loaindg' : 'Get login link'} />
) : null}
{method === 'phone' ? (
<Button text={loading ? 'loading' : 'Get one-time password'} />
) : null}
</form>
</>
)}
<div className="mt-6 relative">
<div className="absolute w-full border-t border-gray-300" />
<div className="relative -top-8 flex items-center justify-center">
<span className="mt-5 bg-white text-gray-500 px-2">
Or Enter With
</span>
</div>
</div>
</div>
</div>
)
}
export default Enter
Notice!!
-여기서는 react-hook-form만 다룸.
-다음Post에서 useMutation을 다루면서 backend랑 통신하는 방법을 알아본다.