react-jsonschema-formにて、formContextを使ってWidgetやFieldに値を渡してみた

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

react-jsonschema-form (RJSF) では、 uiSchemaに自作の option を定義することで、特定のWidgetに独自の値を渡すことができます。

詳細については、Advent Calendar 7日目の記事でふれました。
react-jsonschema-formにて、Widgetをカスタマイズしてみる - メモ的な思考的な

 
ただ、「すべてのWidgetやFieldに独自の値を渡したい」という場合は、自作の option ではなかなか厳しそうです。

そんな場合に使えるのが、Formコンポーネントのprops formContext です。
formContext | <Form /> Props | react-jsonschema-form

公式ドキュメントでは、「各種テーマの設定値をformContextで渡す」例が記載されています。

 
とはいえ、各種テーマ以外でも formContext は使えます。

そこで今回は

  • formContextfontSize を渡す
  • WidgetやFieldで fontSize を取り出し、styleとして設定

を行い、機能全体でフォントサイズを統一できるようにしてみます。

これにより、 formContext の値を自作のWidgetやFieldで使用できることが確認できるはずです。

 
目次

 

環境

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

 

formContext へ設定した値を自作のWidgetやFieldに渡す流れ

WidgetProps や FieldProps の型定義を確認する

せっかくなので、今回はWidgetやFieldのPropsの型である

  • WidgetProps
  • FieldProps

にも型引数を定義してみます。

Githubで型を見ると、それぞれ以下の場所にありました。

 
両方とも型引数は同じで、

  • T
    • formDataの型
  • S
    • RJSFSchemaの型
  • F
    • formContextの型

をそれぞれ渡してあげれば良さそうでした。
Typescript Support | react-jsonschema-form

 

JSON Schemaの用意

今回は inputWidgetinputField の2つの入力項目を持つフォームを用意します。

そのため、今回使用するJSON Schemaは以下となります。

const schema: RJSFSchema = {
  title: "Using Form Context Form",
  type: "object",
  properties: {
    inputWidget: {
      type: "number",
      title: "Input Number Title"
    },
    inputField: {
      type: "string",
    }
  }
}

 

formDataとformContextの型を用意

JSON Schemaにて、入力項目は2つあり、型も定義しました。

それに従い、formDataの型を定義します。

export type UsingFormContextFormField = {
  inputWidget: number
  inputField: string
}

 
また、formContextで fontSize を渡すための型も定義します。

export type MyFormContext = {
  fontSize: number
}

 

Widgetの用意

準備ができたので、まずは自作Widgetを用意します。

WidgetPropsから formContextlabel を取り出し、必要なところへ設定しています。

なお、WidgetPropsから取り出せるものは以下に記載があります。
https://rjsf-team.github.io/react-jsonschema-form/docs/advanced-customization/custom-widgets-fields#adding-your-own-custom-widgets

import {RJSFSchema, WidgetProps} from "@rjsf/utils";
import {MyFormContext, UsingFormContextFormField} from "./UsingFormContextForm";

type Props = WidgetProps<UsingFormContextFormField, RJSFSchema, MyFormContext>

export const MyFormContextWidget = ({formContext: {fontSize}, label, onChange}: Props) => {
  // 元々のonChangeを呼んでformDataに反映する
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value)

  return (
    <>
      <label htmlFor="numberInput" style={{fontSize: fontSize}}>{label}</label>
      <input id="numberInput" type="number" style={{fontSize: fontSize}} onChange={handleChange} />
    </>
  )
}

 

Fieldの用意

続いて、自作Fieldを用意します。

こちらはMUIのTypographyやReactのinputを組み合わせています。

なお、FieldPropsから取り出せるものは以下に記載があります。
https://rjsf-team.github.io/react-jsonschema-form/docs/advanced-customization/custom-widgets-fields#field-props

import {FieldProps, RJSFSchema} from "@rjsf/utils";
import Typography from '@mui/material/Typography';
import {MyFormContext, UsingFormContextFormField} from "./UsingFormContextForm";

type Props = FieldProps<UsingFormContextFormField, RJSFSchema, MyFormContext>

export const MyFormContextField = ({formContext: {fontSize}, title, onChange}: Props ) => {
  // 元々のonChangeを呼んでformDataに反映する
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => onChange(e.target.value)

  return (
    <>
      <Typography
        sx={{
          fontSize: fontSize
        }}
      >
        Typography Title: {title}
      </Typography>
      <input type="string" style={{fontSize: fontSize}} onChange={handleChange} />
    </>
  )
}

 

formContextなどのpropsをFormコンポーネントへ設定

最後に、uiSchema・RegistryWidgetsType・RegistryFieldsTypeなどを用意し、Formコンポーネントのpropsへ設定します。

それに加え、props formContext にも値を設定します。

今回はキー fontSize に対して値 40 を設定し、各WidgetやFieldのfontSizeを 40 に統一してみます。

const widgets: RegistryWidgetsType = {
  myCustomWidget: MyFormContextWidget
}

const fields: RegistryFieldsType = {
  myCustomField: MyFormContextField
}

const uiSchema: UiSchema = {
  inputField: {
    "ui:field": "myCustomField"
  },
  inputWidget: {
    "ui:widget": "myCustomWidget",
  }
}

// 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}
      uiSchema={uiSchema}
      widgets={widgets}
      fields={fields}
      validator={validator}
      onSubmit={onSubmit}
      formContext={{
        fontSize: 40
      }}
    />
  </div>
)

 

動作確認

準備ができたので動作確認します。

すると、WidgetやFieldが formContext から fontSize の値が取得できたようで、各コンポーネントのfontSizeが 40 となりました。

 

ソースコード

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

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