JSON Schemaとreact-jsonschema-form

社内勉強会用資料。社内ツールでJSON Schemaとreact-jsonschema-formを利用しているので、それについての紹介です。

## JSON Schema

[JSON Schema](http://json-schema.org/)

JSONの構造を定義するJSON。

例えば、下記のようなユーザを表すJSONがあるとする。

```json
{
  "id": "00000000-0000-0000-0000-000000000001",
  "name": "Taro YAMADA",
  "age": 30
}
```

- `id`はUUIDで、必須項目
- `name`は英字のみで、必須項目
- `age`は0以上の整数で、任意項目

こういう条件だった場合、JSON Schemaで下記のように定義できる。（あくまで一例だけど）

```json
{
  "title": "ユーザー",
  "type": "object",
  "properties": {
    "id": {
      "title": "ID",
      "type": "string",
      "pattern": "^[0-9a-zA-Z]{8}(-[0-9a-zA-Z]{4}){3}-[0-9a-zA-Z]{12}$"
    },
    "name": {
      "title": "ユーザー名",
      "type": "string",
      "pattern": "^[\\sa-zA-Z]+$"
    },
    "age": {
      "title": "年齢",
      "type": "integer",
      "minimum": 0
    }
  },
  "required": [ "id", "name" ]
}
```

他にも色々と出来るけど、今回は割愛します。一番最初に貼ったリンクで仕様を読むか、下記のドキュメントが分かりやすかったので、そちらで勉強してください。

[Understanding JSON Schema — Understanding JSON Schema 1.0 documentation](https://spacetelescope.github.io/understanding-json-schema/index.html)

## react-jsonschema-form

[mozilla-services/react-jsonschema-form: A React component for building Web forms from JSONSchema.](https://github.com/mozilla-services/react-jsonschema-form)

react-json-schema-formは、JSON Schemaからフォームを生成してくれるReact Component。

Github Pagesに[デモページ](https://mozilla-services.github.io/react-jsonschema-form/)があるので、試しに先程のJSON Schemaを入れてみると下記のようなフォームが生成される。

![image](https://qiita-image-store.s3.amazonaws.com/0/18346/6a3e25e6-b32d-d9dd-8acd-cb8a697fc24a.png)

### 使い方

Formコンポーネントに、下記のようなpropsを渡せば、最低限動作する。その他のpropsについては後述する。

- `schema` ... JSON Schema
- `formData` ... JSON
- `onChange` ... 変更された時に呼ばれるハンドラ
- `onSubmit` ... Submitボタンを押した時に呼ばれるハンドラ

```js
import React from 'react';
import ReactDOM from 'react-dom';
import Form from 'react-jsonschema-form';

const schema = { ... };
const formData = {};

function handleChange({ formData }) {
  // 入力されたデータ
  console.log(formData);
}

function handleSubmit({ formData }) {
  // 入力されたデータ
  console.log(formData);
}

ReactDOM.render(
  <Form
    schema={schema}
    formData={formData}
    onChange={handleChange}
    onSubmit={handleSubmit}
  />,
  document.getElementById('app')
);
```

### カスタマイズ

react-jsonschema-formは、カスタマイズがしやすいのが魅力だと思う。カスタマイズ方法を軽く紹介する。

#### uiSchema

propsに`uiSchema`を渡すと、UI周りのカスタマイズが可能。


- フィールド名、placeholderなどの変更
- デフォルトの`<input>`要素の変更
- class属性の変更

などなど、いろいろできる。わかりやすいやつを一部を紹介。

##### 例1）helpTextの追加

```js
<Form
  schema={{ ...さっきのJSON Schema }}
  uiSchema={{
    "name": {
      "ui:help": "半角英字、スペースのみ"
    }
  }}
/>
```

こうすると、

![image](https://qiita-image-store.s3.amazonaws.com/0/18346/9a08eb53-cd3e-ddec-b88c-d8329e949678.png)

こうなる。


#### 例２)placeholder

```js

<Form
  schema={{ ...さっきのJSON Schema }}
  uiSchema={{
    "name": {
      "ui:placeholder": "半角英字、スペースのみ"
    }
  }}
/>
```

こうすると、

![image](https://qiita-image-store.s3.amazonaws.com/0/18346/3f862194-5a83-0832-65cc-f747918b59a9.png)

こうなる。（エラー起きちゃってるけど）

#### カスタムField

例えば、JSON Schema上は文字列でURLだが、ファイルアップロードするUIを表示したい場合、独自のFiledを表示することができる。

```js
// JSON的にはURLが欲しい
const schema = {
  type: "string",
  format: "uri"
};

// AssetFieldコンポーネントを`asset`という名前で登録する
const customFields = {
  asset: AssetField,
};

// FieldにAssetFieldコンポーネントを使う
const uiSchema = {
  'ui:field': 'asset'
};

// fieldsに渡す
<Form schema={schema} uiSchema={uiSchema} fields={customFields} />
```

AssetFieldでは、下記のようにpropsで渡ってくるonChangeに値を渡して上げればOK。

```js
import React, { Component, PropTypes } from 'react';
import uploadFile from './uploadFile';

class AssetField extends Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    // ファイルをサーバにアップロードして
    // URLを取得するような関数
    uploadFile(e.target.files[0]).then(url => {
      // onChangeで上げる値は、
      // JSON Schema通りの値じゃないと
      // validationでエラーになってしまう
      this.props.onChange(url);
    });
  }

  render() {
    const { required, disabled } = this.props;

    // ファイルアップロードを表示
    return (
      <input
        type="file"
        className="form-control"
        required={required}
        disabled={disabled}
        onChange={this.handleChange}
      />
    );
  }
}

export default AssetField;
```

### おわり

とりあえず、こういうのあるよという紹介。JSON Schemaもreact-jsonschema-formももっと紹介したい機能があるので、またノウハウを貯めて書きたい