開発日誌

開発した時に躓いたこととかの記録

React Hook Formとreact-dropzoneとZodでファイルアップロードフォームを作って、nodemailerでファイルをメール送信する

  • 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-dropzoneのdrop時に、Zodのバリデーションを効かせる

  const onDrop = useCallback(
    (acceptedFiles: File[]): void => {
      setValue('file', acceptedFiles[0], { shouldValidate: true })
    },
    [setValue]
  )

react-dropzoneをpdfのみに制限する

  • inputの時とあまり変わらない
  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    onDrop,
    accept: {
      'application/pdf': ['.pdf'],
    },
  })

formDataの生成

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
}

POSTのheaderの変更

 headers: {
   'Content-Type': 'application/json',
},

api(Serverless Function)側

  • vercelにデプロイするが、vercel serverless functionはあまり大きなファイルを扱うことはできないみたいなので注意
  • バックエンドのAPIとしてのバリデーションもあった方が良いが、今回はその点はSKIP

やっていき

formidableはv3を利用する

Next.jsのbodyParserはOFFにする

  • Next.jsにbodyParserというbodyをいい感じにparseしてくれる機能がありJSONでリクエスト時は大変ありがたいのだが、formでリクエストするときは不都合になるのでオフにする
  • Routing: API Routes | Next.js
export const config = {
  api: {
    bodyParser: false,
  },
}

おわり🎉

  • ファイルアップロードしてメールが送るができるようになりました🎉
  • formまわりの型、とりあえず赤線は解消できたが、違和感があるのでそのうち再チャレンジしたい
  • ファイルアップロードのAPIって結構大変。これももう少し楽な方法ありそうなので探してみたい。