HYUDORO

勉強したことや日記など

YupのSchemaオブジェクトで動的なバリデーションルールを設定する

例えば、最大値をユーザーごとに変えたいケースなど、APIから取得した値を使ってバリデーションしたいことがある。その際に、Schemaのあるファイルとデータを取得するファイルが同じなら、ただ変数を使えばいいのだけど、ファイルが別の場合の対応で悩んだ。

関数を使って渡す

YupのAPIに適した機能がないかを調べたが、Schemaオブジェクト自体はイミュータブルということでざっと見た感じなさそうだった。フォームの値自体をSchemaに渡すことはできるが、それ以外の値はわからん...というのをチームで相談した結果、Schemaオブジェクトをラップする関数を作って、引数をオブジェクトに渡す案が浮かんだ。こんな感じ。

export const getSchema = (
  maxNum: number
): AnyObjectSchema => { // 適切な型がわからなかった...
  return yup.object({
    point: yup
      .number()
      .max(maxNum, `${maxNum}より小さい数を入力してください`)
  });
};

TypeScriptを使ってるので、SchemaオブジェクトからFormDataの型を生成する。返り値のオブジェクトを使うのでReturnTypeを使った。

export type FormData = yup.Asserts<
  ReturnType<typeof getSchema>
>;

react-hook-formを使ってるので、resolverで先ほどの関数を実行する。

  const methods = useForm<FormData>({
    resolver: yupResolver(getSchema(100)),
  });

初期値を設定しておいて、API実行後に値を置き換えたい場合は、引数側でgetSchema(data ?? 100) などとするか、yupのschemaでdefault(0)を使うと良い。