react-jsonschema-formにて、Formコンポーネントのpropsの設定をカスタマイズしてみる

これは「react-jsonschema-formのカレンダー | Advent Calendar 2023 - Qiita」の13日目の記事です。

この記事では、react-jsonschema-form (RJSF) が提供している Form コンポーネントprops の設定をカスタマイズしてみます。

ただ、Formコンポーネントには多くのpropsが存在するため、ここでは気になったpropsのみ扱います。

Props | react-jsonschema-form

 
目次

 

環境

  • react-jsonschema-form 5.15.0
  • React 18.2.0
  • React Router 6.20.1

 

childrenを使って、Formのボタンを増やす

RJSFのデフォルトでは、submitボタンが1つ表示されます。

それ以外にもボタンを増やしたい場合は children が使えます。
https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#children

ここでは

  • my submit
  • my button

の2つのボタンを表示してみます。

import {RJSFSchema} from "@rjsf/utils";
import Form from "@rjsf/mui";  // MUI version
import validator from "@rjsf/validator-ajv8";
import Stack from "@mui/material/Stack";
import Button from "@mui/material/Button";

export const ChildrenForm = () => {
  const schema: RJSFSchema = {
    title: "Children Form",
    type: "object",
    required: [],
    properties: {
      stringInput: {
        type: "string"
      }
    }
  }

  // onSubmitの引数の説明は以下
  // https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#onsubmit
  const onSubmit = ({formData}, _event) => {
    console.log(formData)
  }

  const onClick = () => { console.log("my button") }

  return (
    <div style={{width: '400px'}}>
      <Form
        schema={schema}
        validator={validator}
        onSubmit={onSubmit}
      >
        <Stack spacing={2} direction="row">
          <Button type="submit" variant="contained">MySubmit</Button>
          <Button type="button" variant="outlined" onClick={onClick}>MyButton</Button>
        </Stack>
      </Form>
    </div>
  )
}

 
動かしてみると、ボタンが2つになりました。

また、Formの submit やButtonの onClick も動作しています。

 

extraErrorsとextraErrorsBlockSubmitの挙動について

extraErrors propsを使うことで、RJSF以外で発生したバリデーションエラーもRJSFのフォームに表示できます。
https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#extraerrors

例えば、「バックエンドでバリデーションを実行し、その結果もフォームに表示する」みたいなときに使えそうです。

関係するpropsは

  • extraErrors
  • extraErrorsBlockSubmi

の2つがあるため、それらを見ていきます。

 

extraErrors のみを使うとエラーが表示されるだけでsumitは成功する

まずは extraErrors のみを使う場合です。

以下の実装では、 foobar に対して追加のエラーを表示します。

import {ErrorSchema, RJSFSchema} from "@rjsf/utils";
import Form from "@rjsf/mui";  // MUI version
import validator from "@rjsf/validator-ajv8";

export const ExtraErrorsForm = () => {
  const schema: RJSFSchema = {
    title: "Extra Errors Form",
    type: "object",
    properties: {
      foo: {
        type: "object",
        properties: {
          bar: {
            type: "string",
            const: "bar"
          }
        }
      }
    }
  }

  const onSubmit = ({formData}, _event) => {
    console.log(formData)
  }

  const extraErrors: ErrorSchema = {
    foo: {
      __errors: ["foo error"],
      bar: {
        __errors: ["bar error"]
      }
    }
  }


  return (
    <div style={{width: '400px'}}>
      <Form
        schema={schema}
        validator={validator}
        onSubmit={onSubmit}
        extraErrors={extraErrors}
      />
    </div>
  )
}

 
実行した結果です。最初の時点でエラーが表示されています。

 
ただ、 extraErrors はエラーを表示するだけです。

そのため、JSON Schema的に正しい値を入力した場合にはエラーとはならず、submitが成功します。

 

extraErrorsとextraErrorsBlockSubmitを使うと、エラー解消するまでsubmitが成功しない

もし、 extraErrors に指定したエラーが解消するまでフォームのsubmitが成功しないようにするには、 extraErrorsBlockSubmit propsも指定します。

<Form
  schema={schema}
  validator={validator}
  onSubmit={onSubmit}
  extraErrors={extraErrors}
  extraErrorsBlockSubmit // 追加
/>

 
動かしてみると、extraErrorsにデータがある場合はsubmitが成功しなくなりました。

 

focusOnFirstErrorについて

RJSFでは、最初にエラーが発生した項目にフォーカスを当てるための props focusOnFirstError があります。
https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#focusonfirsterror

このpropsにはbooleanの他にコールバックも渡せるため、各挙動を確認してみます。

 

focusOnFirstErrorに true を渡した時の挙動

focusOnFirstErrortrue にした場合の挙動を確認します。

import {RJSFSchema} from "@rjsf/utils";
import Form from "@rjsf/mui";  // MUI version
import validator from "@rjsf/validator-ajv8";

export const FocusOnFirstErrorForm = () => {
  const schema: RJSFSchema = {
    title: "Focus On First Error Form",
    type: "object",
    required: [],
    properties: {
      foo: {
        type: "string",
        const: "foo"
      },
      bar: {
        type: "string",
        const: "bar"
      },
      baz: {
        type: "string",
        const: "baz"
      }
    }
  }

  return (
    <div style={{width: '400px'}}>
      <Form
        schema={schema}
        validator={validator}
        focusOnFirstError
      />
    </div>
  )
}

 
動かしてみます。

上と真ん中がエラーになるようにしてsubmitすると、上の項目にフォーカスが当たります。

 
一方、真ん中と下がエラーになるようにしてsubmitすると、真ん中の項目にフォーカスが当たります。

 

focusOnFirstErrorにコールバックを渡した時の挙動

続いて、 focusOnFirstError にコールバックを渡した時の挙動を確認します。

JSON Schemaはそのままで、コールバックを追加しFormコンポーネントに渡します。

const callback = (error: RJSFValidationError) => console.log(error)

return (
  <div style={{width: '400px'}}>
    <Form
      schema={schema}
      validator={validator}
      focusOnFirstError={callback}
    />
  </div>
)

 
動かしてみます。

今回も、上と真ん中の項目がエラーになるよう入力してsubmitしたところ、

  • フォーカスはどこにも当たらない
  • コールバックが実行され、上の項目のエラー内容がコンソールへ出力

となりました。

 

onErrorについて

onError propsでは、フォームをsubmitしたときに呼ばれるコールバックを指定できます。
https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#onerror

このpropsについて気になる部分を確認します。

 

onErrorに何も設定しないとエラーがコンソール出力される

今までも所々で見えたのですが、RJSFの場合、バリデーションエラーが発生するとブラウザのコンソールにもエラーの内容が出力されます。

例えば、先ほどの focusOnFirstError propsでエラーを発生させたときには、以下のような内容がコンソール出力されていました。

 
これは、Formコンポーネントにて「 onError propsを設定しない状態でエラーが発生した場合、エラーをコンソール出力する」と実装されていることが原因です。
https://github.com/rjsf-team/react-jsonschema-form/blob/v5.15.1/packages/core/src/components/Form.tsx#L827

 
そのため、もしコンソールにバリデーションを出力したくない場合には、 onError に何もしない関数を渡すなどの対応が必要です。

例えば、以下のように onError へ何もしない関数を割り当てます。

import {RJSFSchema} from "@rjsf/utils";
import Form from "@rjsf/mui";  // MUI version
import validator from "@rjsf/validator-ajv8";

export const OnErrorForm = () => {
  const schema: RJSFSchema = {
    title: "On Error Form",
    type: "object",
    required: [],
    properties: {
      foo: {
        type: "string",
        const: "foo"
      }
    }
  }

  return (
    <div style={{width: '400px'}}>
      <Form
        schema={schema}
        validator={validator}
        onError={()=> {}}
      />
    </div>
  )
}

 
すると、フォームにはエラーが出力されますが、コンソールには何も出力されなくなります。

 

focusOnFirstError にコールバックを渡しつつ、onErrorを設定した場合の挙動

ここまでで、バリデーションエラーをハンドリングできる props として

  • focusOnFirstError
  • onError

の2つがあると分かりました。

では、両方同時に設定したときにどのような挙動になるか見てみます。

import {RJSFSchema, RJSFValidationError} from "@rjsf/utils";
import Form from "@rjsf/mui";  // MUI version
import validator from "@rjsf/validator-ajv8";

export const FocusOnFirstErrorAndOnErrorForm = () => {
  const schema: RJSFSchema = {
    title: "Focus On First Error And On Error Form",
    type: "object",
    required: [],
    properties: {
      foo: {
        type: "string",
        const: "foo"
      },
      bar: {
        type: "string",
        const: "bar"
      },
      baz: {
        type: "string",
        const: "baz"
      }
    }
  }

  const callback = (error: RJSFValidationError) => console.log("callback",  error)

  const onError = (error: RJSFValidationError[]) => console.log("onError", error)

  return (
    <div style={{width: '400px'}}>
      <Form
        schema={schema}
        validator={validator}
        focusOnFirstError={callback}
        onError={onError}
      />
    </div>
  )
}

 
動かしてみたところ、両方とも動作しました。違いは

  • focusOnFirstError のコールバックには、最初のエラーのみが渡される
  • onError のコールバックには、すべてのエラーが渡される

でした。

 

noValidateの挙動とdeprecated

RJSFでフォームだけ用意してバリデーションをしたくない場合、 noValidate props に true を渡します。
https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#novalidate

ちなみに、公式ドキュメントには記載がありませんが、ソースコードを見ると noValidate はdeprecatedになっていました。
https://github.com/rjsf-team/react-jsonschema-form/blob/v5.15.1/packages/core/src/components/Form.tsx#L156-L158

そのため、将来的には使えなくなる props のようです。

では実装です。

import {RJSFSchema} from "@rjsf/utils";
import Form from "@rjsf/mui";  // MUI version
import validator from "@rjsf/validator-ajv8";

export const NoValidateForm = () => {
  const schema: RJSFSchema = {
    title: "No Validate Form",
    type: "object",
    required: [],
    properties: {
      stringInput: {
        type: "string",
        format: "uuid"
      }
    }
  }

  const onSubmit = ({formData}, _event) => {
    console.log(formData)
  }

  return (
    <div style={{width: '400px'}}>
      <Form
        schema={schema}
        validator={validator}
        onSubmit={onSubmit}
        noValidate
      />
    </div>
  )
}

 
動かしてみると、バリデーションエラーにならず、submitした時のログがコンソールへと出力されました。

 

omitExtraDataとは何かとその挙動について

最初、 omitExtraData propsとは何か分からなかったため、公式ドキュメントを読んだところ

If set to true, then extra form data values that are not in any form field will be removed whenever onSubmit is called. Set to false by default.

https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#omitextradata

とありました。

これより、 extra form data values that are not in any form field は、Form の formData propsで渡すデータと考えました。
https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#formdata

 
そこで、Formに formData でフォームの項目としては存在していないデータを渡した上で、 omitExtraDatatrue にしたときに、どのような挙動になるかを見てみます。

 

formData のみ設定する場合の挙動

まずは、 formData のみ設定する場合です。この場合、 omitExtraData はデフォルトの false になります。

import {RJSFSchema} from "@rjsf/utils";
import Form from "@rjsf/mui";  // MUI version
import validator from "@rjsf/validator-ajv8";

export const OmitExtraDataForm = () => {
  const schema: RJSFSchema = {
    title: "Omit Extra Data Form",
    type: "object",
    required: [],
    properties: {
      stringInput: {
        type: "string",
      }
    }
  }

  const onSubmit = ({formData}, _event) => {
    console.log(formData)
  }

  return (
    <div style={{width: '400px'}}>
      <Form
        schema={schema}
        validator={validator}
        onSubmit={onSubmit}
        formData={{
          stringInput: "foo",
          noFieldData: "nothing"
        }}
      />
    </div>
  )
}

 
動かしてみます。

何も変更せずにsubmitすると、formDataで設定した項目のうち、フォームの項目として存在しないものもコンソールへ出力されました。

 

formData と omitExtraData を設定する場合の挙動

続いて、 formDataomitExtraData の両方を設定してみます。

<Form
  schema={schema}
  validator={validator}
  onSubmit={onSubmit}
  formData={{
    stringInput: "foo",
    noFieldData: "nothing"
  }}
  omitExtraData  // 追加
/>

 
ここでも何も変更せずにsubmitしたところ、入力項目のみコンソールへ出力されました。

 

showErrorListにて、エラーリストの表示位置を変更する

showErrorList propsを使うことにより、エラーリストの表示位置を変更できます。
https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#showerrorlist

デフォルトでは画面上部に表示するため、それ以外の表示を確認します。

 

画面下部にエラーリストを表示する

showErrorListbottom にします。

<Form
  schema={schema}
  validator={validator}
  showErrorList={"bottom"}
/>

 
バリデーションエラーにしたところ、画面下部にエラーリストが表示されました。

 

画面にエラーリストを表示しない

この場合は、 false を設定します。

<Form
  schema={schema}
  validator={validator}
  showErrorList={false}
/>

 
バリデーションエラーにしたところ、画面にエラーリストは表示されませんでした。

 

ソースコード

Githubにあげました。
https://github.com/thinkAmi-sandbox/rjsf_advent_calendar_2023

今回のプルリクはこちら。
https://github.com/thinkAmi-sandbox/rjsf_advent_calendar_2023/pull/12