EnterForm(react-hook-form)

김종민·2022년 8월 1일
0

apple-market

목록 보기
11/37

https://velog.io/@silverbeen/Naver-Map-%EC%9E%90%EC%9C%A0%EB%A1%AD%EA%B2%8C-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0

->naver지도만들기

들어가기
enter Form을 만들어본다
react-hook-form을 이용해서
Input component에 react-hook-form을 적용시켜 보자~

1. components/input.tsx

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>
  )
}

2. pages/enter.tsx

집중해서 봐야 될것이 많지만, 여기서는 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랑 통신하는 방법을 알아본다.

profile
코딩하는초딩쌤

0개의 댓글