react-jsonschema-formにおける、バリデーションエラー時の気になる挙動と対応について

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

react-jsonschema-form (RJSF) では、JSON Schemaを元にバリデーションができます。

ただ、バリデーションエラーとなったときに気になる挙動がいくつかあったため、メモを残します。

 
目次

 

環境

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

 

formDataをstateとして保持していると、バリデーションエラー後、再入力してもバリデーションエラーが消えない

現象

RJSFでは、バリデーションエラーになった後、そのエラーとなった項目に再入力することでエラーが消えるような挙動をします。

バリデーションエラー発生

 
1文字入力すると、エラーが消える

 
ただ、

  • フォームの入力値である formDatauseState を使ったstateとして保持
  • フォームにはクリアボタンやデータ更新ボタンがある
    • このボタンの中でstateを更新する

とした場合、バリデーションエラー発生後に再度入力してもエラーが残り続けます。

 
例えば、以下のようなJSON SchemaとFormがあったとします。

import {RJSFSchema} from "@rjsf/utils";
import Form from "@rjsf/mui";
import validator from "@rjsf/validator-ajv8";
import Stack from "@mui/material/Stack";
import {useState} from "react";

type MyFormData = {
  stringInput: string
}

export const NotClearErrorForm = () => {
  const [formData, setFormData] = useState<MyFormData>({
    stringInput: "foo"
  })

  const schema: RJSFSchema = {
    title: "Not Clear Error Form",
    type: "object",
    properties: {
      stringInput: {
        type: "string",
        minLength: 5
      }
    }
  }

  // https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript/8084248
  const onLoadData = () => setFormData({stringInput: Math.random().toString(32).substring(2) })
  const onClear = () => setFormData({stringInput: ""})

  return (
    <div style={{width: '400px'}}>
      <Form
        schema={schema}
        formData={formData}
        validator={validator}
      >
        <Stack direction={"row"} spacing={2}>
          <button type="submit">Submit</button>
          <button type="button" onClick={onLoadData}>Load another data</button>
          <button type="button" onClick={onClear}>Clear</button>
        </Stack>
      </Form>
    </div>
  )
}

 
stateをFormの formData に渡しているため、初期表示ではデータが存在します。

 
この状態でsubmitすると、JSON Schemaの minLength: 5 に違反しているため、バリデーションエラーになります。

 
しかし、バリデーションエラー後、Load another data ボタンを押しても、バリデーションエラーは消えません。

 
その後、再度入力するとバリデーションエラーが消えます。

上記の例では、 X を追加入力したところエラーが消えました。

 
これは clear ボタンを押したときも同様です。

 

対応

Githubのissueにコメントがありました。

Formの key を更新することで、Formのインスタンスを差し替えれば回避できるそうです。

From what I can see so far, errors are internal state so there is no way to clear it except creating a new form instance by changing element key.

Form errors are not updated/cleared after new formData are passed to the form · Issue #486 · rjsf-team/react-jsonschema-form

 
そこで、

  • stateに formDatakey の2つの値をもたせる
  • ボタンクリック時に、 formDataに加え、keyも更新する

とします。

import {RJSFSchema} from "@rjsf/utils";
import Form from "@rjsf/mui";
import validator from "@rjsf/validator-ajv8";
import Stack from "@mui/material/Stack";
import {useState} from "react";

type MyFormData = {
  stringInput: string
}

type State = {
  key: Date
  formData: MyFormData
}

export const NotClearErrorFormFixed = () => {
  // 型と初期値にkeyを加える
  const [data, setData] = useState<State>({
    key: new Date(),
    formData: {
      stringInput: "foo"
    }
  })

  const schema: RJSFSchema = {
    title: "Fixed: Not Clear Error Form",
    type: "object",
    properties: {
      stringInput: {
        type: "string",
        minLength: 5
      }
    }
  }

  // keyも更新するよう変更
  const onLoadData = () => setData({key: new Date(), formData: { stringInput: Math.random().toString(32).substring(2) }})
  const onClear = () => setData({key: new Date(), formData: {stringInput: ""}})

  return (
    <div style={{width: '400px'}}>
      <Form
        key={data.key}  // 追加
        schema={schema}
        formData={data.formData}
        validator={validator}
      >
        <Stack direction={"row"} spacing={2}>
          <button type="submit">Submit</button>
          <button type="button" onClick={onLoadData}>Load another data</button>
          <button type="button" onClick={onClear}>Clear</button>
        </Stack>
      </Form>
    </div>
  )
}

 
すると、Clearなどのボタンを押したタイミングでエラーメッセージも消えるようになりました。

 

日付データをformDataとして設定する時のフォーマットについて

現象

RJSFを使う際、外部APIから取得したデータを初期値としてFormの formData へ設定することがあります。

この時、 2023/12/22 のようなスラッシュ区切りとして設定すると、 formData に値を設定したにも関わらず、画面には表示されません。

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

export const DateFormatForm = () => {
    const schema: RJSFSchema = {
    title: "Date Format Form",
    type: "object",
    properties: {
      dateInput: {
        type: "string",
        format: "date"
      }
    }
  }

  return (
    <div style={{width: '400px'}}>
      <Form
        schema={schema}
        validator={validator}
        formData={{
          dateInput: '2023/12/22'
        }}
      />
    </div>
  )
}

 
表示すると、初期値に何も設定されていません。

 

対応

ブラウザのコンソールを見るとメッセージが出ています。

The specified value "2023/12/12" does not conform to the required format, "yyyy-MM-dd".

 
そこで、 formData- 区切りで設定します。

<Form
  schema={schema}
  validator={validator}
  formData={{
    dateInput: '2023-12-22'
  }}
/>

 
すると、画面に初期値が表示されました。

 

バリデーションエラーになると、ブラウザのコンソールにエラーが出力される

この件は13日目の記事に書きましたので、そちらを参照ください。
onErrorについて | react-jsonschema-formにて、Formコンポーネントのpropsの設定をカスタマイズしてみる - メモ的な思考的な

 

ソースコード

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

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