現在當選擇類別後,不管輸入什麼文字,結果都可以送出表單,並且產生錯誤的 QR code,這樣並不是我們想要的結果,所以這篇文章將會示範如何設定正規表達式來驗證輸入的文字。
在 React Hook Form 中,可以使用 pattern
來設定正規表達式,所以我們可以在 FormInputs
中加入 pattern
來設定正規表達式:
const TextInput: FC<TextInputProps> = ({ register, pattern, errors }) => (
<div className='mb-4'>
<label className='block font-bold mb-2'>輸入文字</label>
<input
type='text'
className='w-full p-2 border rounded focus:outline-none focus:ring focus:border-blue-300'
{...register('text', {
required: true,
pattern:
/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
})}
/>
</div>
)
這裡使用了 pattern: /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/
來驗證輸入的文字是否為 URL,如果不是的話,就會顯示錯誤訊息。
所以現在當輸入錯誤的 URL 時,就不會送出表單。
不過這裡有一個問題,就是如果選擇其他的類別時,就沒有辦法正確驗證相對應的格式,例如選擇電話,輸入正確的電話格式,也沒辦法送出表單,所以我們需要動態設定 pattern
。
在 page.tsx
先從 useForm
中取出 watch
,並且監聽 qrType
的變化。然後設定一個 pattern
的物件,裡面包含了各個類別的正規表達式:
const qrType = watch('qrType', 'URL') // 記得預設為 URL
const validationPatterns = {
URL: /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/,
電話: /^(\+?\d{1,3}[-.\s]?)?\d{10}$/,
地址: /.+/,
Email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
}
const pattern = validationPatterns[qrType as keyof typeof validationPatterns]
然後使用 keyof typeof
來取得 validationPatterns
的 key 的型別,並且使用 qrType as keyof typeof validationPatterns
來設定 pattern
的型別。
接下來就可以在 TextInput
傳入 pattern
:
<TextInput register={register} pattern={pattern} />
繼續設定 TextInput
:
interface TextInputProps {
register: UseFormRegister<FormInputs>
pattern: RegExp
}
const TextInput: FC<TextInputProps> = ({ register, pattern }) => (
<div className='mb-4'>
<label className='block font-bold mb-2'>輸入文字</label>
<input
type='text'
className='w-full p-2 border rounded focus:outline-none focus:ring focus:border-blue-300'
{...register('text', { required: true, pattern })}
/>
</div>
)
這樣子就可以動態地按照選取的類別,驗證輸入的文字是否符合設定的格式。
現在雖然可以驗證輸入的文字,但是並沒有顯示錯誤訊息,所以我們需要 React Hook Form 提供的 errors
來顯示錯誤訊息。
一樣先從 page.tsx
取出 errors
:
const {
register,
handleSubmit,
setValue,
watch,
formState: { errors }
} = useForm<FormInputs>()
並宣告一個變數,來判斷是否有錯誤,等一下會用到:
const hasErrors = Object.keys(errors).length > 0
然後在 TextInput
傳入 errors
,還有送出表單按鈕,以及新增一些提示訊息:
<main className='flex min-h-screen flex-col items-center justify-between p-24'>
<div className='container mx-auto p-4'>
<h1 className='text-3xl mb-4'>QR Code 製造器</h1>
<form onSubmit={handleSubmit(onSubmit)} className='flex flex-col gap-y-5'>
<SelectType register={register} />
<TextInput register={register} pattern={pattern} errors={errors} />
<div className='flex items-center justify-evenly'>
<SizeSlider register={register} setValue={setValue} />
<ColorPicker
register={register}
label='選擇顏色'
name='qrColor'
setValue={setValue}
/>
<ColorPicker
register={register}
label='選擇背景顏色'
name='qrBgColor'
setValue={setValue}
/>
</div>
<div className='flex justify-center mt-5'>
<button
type='submit'
className={`${
hasErrors ? 'bg-gray-400' : 'bg-green-500'
} hover:bg-green-100 text-white hover:text-slate-700 font-bold py-2 px-4 rounded text-center`}
>
產生 QR Code
</button>
</div>
</form>
{imgSrc && Object.keys(errors).length < 1 ? (
<div className='flex justify-center mt-10'>
<Image src={imgSrc} width={500} height={500} alt='QR Code Image' />
</div>
) : (
<p>{hasErrors ? '請輸入正確的資訊' : '請點擊「產生 QR Code」按鈕'}</p>
)}
</div>
</main>
TextInput
這部分也要設定 errors
的型別,以及修改一下錯誤的樣式:
import { FC } from 'react'
import { UseFormRegister, FieldErrors } from 'react-hook-form'
import { FormInputs } from '../page'
interface TextInputProps {
register: UseFormRegister<FormInputs>
pattern: RegExp
errors: FieldErrors<FormInputs>
}
const TextInput: FC<TextInputProps> = ({ register, pattern, errors }) => (
<div className='mb-4'>
<label className='block font-bold mb-2'>輸入文字</label>
<input
type='text'
className={`w-full p-2 border rounded focus:outline-none focus:ring ${
errors?.text
? 'focus:border-red-500 border-red-500 ring-red-500'
: 'focus:border-blue-300'
}`}
{...register('text', { required: true, pattern })}
/>
</div>
)
到這裡就完成了,現在可以正確地驗證輸入的文字,並且顯示錯誤訊息。
雖然現在已經可以做到按照選擇類別去規範輸入的文字,輸入正確才能顯示 QR code,不過有一個問題是,當得到正確的 QR code 後,再次選擇不同類別,並輸入不符合的規範後,確實能夠擋下來,但是當輸入正確的格式後,會立刻顯示上一個 QR code,這時候都還沒按送出表單的按鈕,那麼有什麼方式可以解決?
其實蠻簡單的,只要在選擇時,把 imgSrc
清空就可以了:
import { useState, useEffect } from 'react'
// 省略其他程式碼
const [imgSrc, setImgSrc] = useState<string | null>(null)
const qrType = watch('qrType', 'URL')
useEffect(() => {
setImgSrc(null)
}, [qrType])
現在這個 QR code 製造器已經可以正確地驗證輸入的文字,並且顯示錯誤訊息,也可以正確地顯示 QR code,並且可以動態地切換類別,前端的部分暫時完成了!明天將會接續實作後端的部分,明天見👋!