これは「react-jsonschema-formのカレンダー | Advent Calendar 2023 - Qiita」の16日目の記事です。
以下のようなJSON Schemaからreact-jsonschema-form (RJSF) が生成したフォームをsubmitするとします。
const schema: RJSFSchema = { title: "No Input Form", type: "object", properties: { stringInput: { type: "string", } } }
動かしてみる前は、送信されるデータは「キー stringInput
、データは空文字」と思っていました。
しかし、実際には空オブジェクトが送信されました。
これは、公式ドキュメントの
When a text input is empty, the field in form data is set to undefined.
The case of empty strings | Validation | react-jsonschema-form
という挙動に一致してそうでした。
そんな中、「JSON Schemaやsubmitするタイミングによって送信データが変わるのか」が気になったことから、ためしてみることにしました。
なお、今回は RJSF + RJSFのMUIテーマにて動作を確認しています。
目次
環境
- react-jsonschema-form 5.15.0
- React 18.2.0
- React Router 6.20.1
何も入力せずsubmitするパターン
まずは、JSON SchemaからRJSFが生成したフォームに対して、何も入力せずにsubmitした時の挙動を見てみます。
JSON Schemaのdefaultを指定していない場合
今回は色々な type
での動作を確認してみたいことから、以下のようなJSON Schemaなどを用意しました。
import {RJSFSchema} from "@rjsf/utils"; import Form from "@rjsf/mui"; // MUI version import validator from "@rjsf/validator-ajv8"; export const NoInputWithoutDefaultForm = () => { const schema: RJSFSchema = { title: "No Input Without Default Form", type: "object", required: [], properties: { stringInput: { type: "string", }, integerInput: { type: "integer" }, numberInput: { type: "number" }, booleanInput: { type: "boolean" }, nullInput: { type: "null" }, arrayInput: { type: "array", items: { type: "object", properties: { stringInput: { type: "string" }, integerInput: { type: "integer" } } } }, enumLabelWithOneOf: { type: "string", oneOf: [ { "const": "foo", title: "foo label" }, { "const": "bar", title: "bar label" }, { "const": "baz", title: "baz label" }, ] }, } } // onSubmitの引数の説明は以下 // https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props#onsubmit const onSubmit = ({formData}, _event) => { console.log(formData) } return ( <div style={{width: '400px'}}> <Form schema={schema} validator={validator} onSubmit={onSubmit} /> </div> ) }
submitしてみると
type = "null"
はキーと値(null)が送信されたtype = "array"
はキーと空配列が送信された
となりました。
JSON Schemaのdefaultにundefinedを指定する場合
続いて、JSON Schemaの default
に値を設定したときの挙動を見てみます。
まずは undefined
にした時の挙動です。
const schema: RJSFSchema = { title: "No Input With Default Undefined Form", type: "object", required: [], properties: { stringInput: { type: "string", default: undefined, }, integerInput: { type: "integer", default: undefined, }, numberInput: { type: "number", default: undefined, }, booleanInput: { type: "boolean", default: undefined, }, nullInput: { type: "null" }, arrayInput: { type: "array", items: { type: "object", properties: { stringInput: { type: "string", default: undefined, }, integerInput: { type: "integer", default: undefined, } } } }, enumLabelWithOneOf: { type: "string", default: undefined, oneOf: [ { "const": "foo", title: "foo label" }, { "const": "bar", title: "bar label" }, { "const": "baz", title: "baz label" }, ] }, } }
submitしてみると、 default
無しと同じ挙動となりました。
JSON Schemaのdefaultにnullを指定する場合
続いて、JSON Schemaの default
に null
を指定してみます。
なお、 default
に null
を指定する場合は、type
へ ["string", null]
とnullableな型を指定します。
const schema: RJSFSchema = { title: "No Input With Default Null Form", type: "object", required: [], properties: { stringInput: { type: ["string", "null"], default: null, }, integerInput: { type: ["integer", "null"], default: null, }, numberInput: { type: ["number", "null"], default: null, }, booleanInput: { type: ["boolean", "null"], default: null, }, nullInput: { type: "null" }, arrayInput: { type: "array", items: { type: "object", properties: { stringInput: { type: ["string", "null"], default: null, }, integerInput: { type: ["integer", "null"], default: null, } } } } } }
sumbmitしてみると、各項目のキーが設定され、値はnullになっていました。
JSON Schemaのdefaultにfalsyな値を指定する場合
今度は JSON Schemaの default
に falsy な値を設定してみます。
const schema: RJSFSchema = { title: "No Input With Default Falsy Form", type: "object", required: [], properties: { stringInput: { type: "string", default: "", }, integerInput: { type: "integer", default: 0, }, numberInput: { type: "number", default: 0, }, booleanInput: { type: "boolean", default: false, }, nullInput: { type: "null" }, arrayInput: { type: "array", items: { type: "object", properties: { stringInput: { type: "string", default: "", }, integerInput: { type: "integer", default: 0, } } } } } }
submitすると、falsyな値が設定されていました。
Formのprops「formData」にキーとundefinedを設定する場合
RJSFのpropsには formData
があります。これを使うことで、フォームに対し初期値を与えることができます。
formData | <Form />
Props | react-jsonschema-form
そこで、 formData
propsに対して、一部のキーに対してundefinedを設定した時の挙動をためしてみます。
以下の例では、formData
に対し
stringInput
のみundefined
な値を設定- フォームに存在しない
noField
というキーも設定
としています。
import {RJSFSchema} from "@rjsf/utils"; import Form from "@rjsf/mui"; // MUI version import validator from "@rjsf/validator-ajv8"; export const NoInputWithFormData = () => { const schema: RJSFSchema = { title: "No Input With Form Data", type: "object", required: [], properties: { stringInput: { type: "string", }, integerInput: { type: "integer" }, numberInput: { type: "number" }, booleanInput: { type: "boolean" }, nullInput: { type: "null" }, arrayInput: { type: "array", items: { type: "object", properties: { stringInput: { type: "string" }, integerInput: { type: "integer" } } } } } } const onSubmit = ({formData}, _event) => { console.log(formData) } return ( <div style={{width: '400px'}}> <Form schema={schema} validator={validator} onSubmit={onSubmit} formData={{ stringInput: undefined, noField: undefined }} /> </div> ) }
submitしてみると、formData
で設定したキーと値が送信されています。
フォームに存在するキーであれば、値が undefined
でも送信されるようです。
なお、フォームの項目として存在していないキー noField
も送信されています。
これを送信したくない場合は、Formのprops omitExtraData
を true
にすれば良いです(以前の記事で確認しています)。
https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/form-props/#omitextradata
いったん項目に入力した後、その入力を削除してsubmitするパターン
ここまでは、画面表示後に何も入力せずにフォームをsubmitした時の挙動を確認しました。
次は、いったん項目に入力した後、その入力を削除してsubmitするパターンをみてみます。
動作を確認したところ、入力した項目が undefined
で送信されました。
項目への入力有無で挙動が変わるようです。
その他
oneOf + emptyValueを使う場合の挙動
最後はここまでと異なる内容ですが、フォームの入力まわりということで記載します。
RJSFのデフォルトのテーマとRJSFのMUIテーマでは、 oneOf
+ emptyValue
を使う時の挙動が異なったため、メモしておきます。
なお、
String fields that use enum and a select widget will have an empty option at the top of the options list that when selected will result in the field being null.
One consequence of this is that if you have an empty string in your enum array, selecting that option in the select input will cause the field to be set to null, not an empty string.
If you want to have the field set to a default value when empty you can provide a ui:emptyValue field in the uiSchema object.
https://rjsf-team.github.io/react-jsonschema-form/docs/usage/validation#the-case-of-empty-strings
と公式ドキュメントにあるように、emptyValue
は「 enum
のような形で select タグを描画した時に、一番上の何も選択しないを選んだ時に選ばれる設定」という理解です。
RJSFのデフォルトのテーマの場合
まず、RJSFのデフォルトのテーマの場合の挙動を確認してみます。
JSON Schemaでselectタグを描画するような定義にします。
合わせて、uiSchemaで emptyValue: "foo"
として「何も選択しない場合は foo を選択したものとする」定義にします。
const schema: RJSFSchema = { title: "Empty Value With Original Theme", type: "object", required: [], properties: { enumLabelWithOneOf: { type: "string", oneOf: [ { "const": "foo", title: "foo label" }, { "const": "bar", title: "bar label" }, { "const": "baz", title: "baz label" }, ], } } } const uiSchema: UiSchema = { enumLabelWithOneOf: { "ui:options": { emptyValue: "foo" } } }
動かしてみると、
- 先頭の空行を選んだときは、空行を選んだまま
- 先頭の空行から別の行を選び、再度空行を選ぶと、
emptyValue
で定義した行が選択される - そのままsubmitすると、
emptyValue
で定義した行の値が送信される
となりました。
RJSFのMUIテーマの場合
続いて、RJSFのMUIテーマの場合です。
RJSFのデフォルトのテーマからの変更点は import
だけを差し替えただけになります。
// RJSFのデフォルトテーマ // import Form from "@rjsf/core"; // RJSFのMUIテーマ import Form from "@rjsf/mui";
動かしてみると、RJSFのデフォルトのテーマと異なり空行が選べません。
また、空行以外の行を選択してsubmitすると、選択行の値が送信されました。
ソースコード
Githubにあげました。
https://github.com/thinkAmi-sandbox/rjsf_advent_calendar_2023
今回のプルリクはこちら。
https://github.com/thinkAmi-sandbox/rjsf_advent_calendar_2023/pull/14