- React Hook Formとreact-dropzoneを組み合わせた時にバリデーションを発火させるのに手間取ったのでメモ。知ってれば難しいことはない。
- Next.jsのAPIでnodemailerを使ってメールを送るのアップデート版
- textareaのinputをメール送信するだけの機能に、下記の要素を追加
- 選択肢を追加
- ファイルのアップロードを追加(pdfに制限する)
- バリデーションを追加
- 機能の実現のために、下記のライブラリを追加した
- React Hook Form: form項目増えるので管理のため
- react-dropzone: ファイルアップロードをドラッグ&ドロップで実現したいため
- Zod: バリデーション機能のため
- formidable: formのデータ受信のため
- リポジトリ
UI側
ライブラリ選定理由
- 元々Formik & Yupの組み合わせを使ってたが、最近はReact Hook Form & Zodの波がキてると聞いてノってみた
- React Hook Form & Zodの方が軽くてパフォーマンスにも優れてるようなので、使い心地を試したい
やっていき
- 全部はると長いのでリンクで
- React Hook Form, Zod, react-dropzone、どれも学習難度は低く書き方は直感的で良い感じ
- 参考サイト
react-dropzoneのdrop時に、Zodのバリデーションを効かせる
const onDrop = useCallback(
(acceptedFiles: File[]): void => {
setValue('file', acceptedFiles[0], { shouldValidate: true })
},
[setValue]
)
react-dropzoneをpdfのみに制限する
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: {
'application/pdf': ['.pdf'],
},
})
interface SchemaInterface {
[key: string]: string | number | Blob
}
interface CustomFormData extends FormData {
append<T extends string | Blob>(
name: keyof Schema,
value: T,
fileName?: string
): void
}
const createFormDatata = (data: Schema) => {
const formData = new FormData() as CustomFormData
Object.keys(data).forEach((key: string) => {
const subKey = key as keyof Schema
const d = data as SchemaInterface
const tmp = d[subKey]
if (typeof tmp === 'number') {
formData.append(subKey, String(tmp))
} else {
formData.append(subKey, tmp)
}
})
return formData
}
headers: {
'Content-Type': 'application/json',
},
api(Serverless Function)側
- vercelにデプロイするが、vercel serverless functionはあまり大きなファイルを扱うことはできないみたいなので注意
- バックエンドのAPIとしてのバリデーションもあった方が良いが、今回はその点はSKIP
やっていき
- こちらも長いのでリンクで
formidable
を利用してformをparseして、データを詰め込んでいく
- 参考サイト
Next.jsのbodyParserはOFFにする
export const config = {
api: {
bodyParser: false,
},
}
おわり🎉
- ファイルアップロードしてメールが送るができるようになりました🎉
- formまわりの型、とりあえず赤線は解消できたが、違和感があるのでそのうち再チャレンジしたい
- ファイルアップロードのAPIって結構大変。これももう少し楽な方法ありそうなので探してみたい。