Formikで利用する場合のYupのカスタマイズ

#はじめに
今まで `react-redux-form`を使ってたのですが、公式から[もう使うなって](https://github.com/davidkpiano/react-redux-form)言われてしまったので`Formik`に書き換えてみました。その際バリデーションのコントロールを行う為に[Yup](https://github.com/jquense/yup)をカスタマイズしたのでその内容をメモしておきます。`Formik`での具体的な`Yup`の利用方法はここでは記載しません。


#カスタマイズの基本

```js:yup.custom.js
import * as Yup from 'yup';

//カスタマイズを記述

export default Yup;
```

で、`yup`を利用するときにはこのカスタマイズファイルを利用します。

```js:
import Yup from '/path/to/yup.custom.js';
```

#日本語化
公式サンプルだと`TypeScript`なんですよね。

```js:
Yup.setLocale({
  mixed: {
    default: '入力エラーです',
    required: '必須入力項目です',
    oneOf: ({ values }) => `次の値のいずれかでなければなりません: ${values}`,
    notOneOf: ({ values }) => `次の値のいずれかであってはなりません: ${values}`,
    notType: '形式が違います',
  },
  string: {
    length: ({ length }) => `${length}文字入力して下さい`,
    min: ({ min }) => `${min}文字以上入力してください`,
    max: ({ max }) => `${max}文字以内で入力して下さい`,
    matches: '形式が違います',
    email: '形式が違います',
    url: '形式が違います',
    trim: '前後にスペースを入れてはいけません',
    lowercase: '小文字でなければなりません',
    uppercase: '大文字でなければなりません',
  },
  number: {
    min: ({ min }) => `${min}以上の値を入力して下さい`,
    max: ({ max }) => `${max}以下の値を入力して下さい`,
    lessThan: ({ lessThan }) => `${lessThan}より小さい値を入力して下さい`,
    moreThan: ({ moreThan }) => `${moreThan}より大きい値を入力して下さい`,
    notEqual: ({ notEqual }) => `${notEqual}と違う値を入力して下さい`,
    positive: '正の数を入力して下さい',
    negative: '負の数を入力して下さい',
    integer: '整数を入力して下さい',
  },
  date: {
    default: '形式が違います',
    min: ({ min }) => `${min}以上の日付を入力して下さい`,
    max: ({ max }) => `${max}以下の日付を入力して下さい`,
  },
  object: {
    noUnknown: '有効なキーを持ったデータを入力して下さい',
  },
  array: {
    min: ({ min }) => `${min}個以上の値を入力して下さい`,
    max: ({ max }) => `${max}個以下の値を入力して下さい`,
  },
});
```

引数として `path`を取ると、例えば

```js
length:({path,length}) =>`${path}には${length}文字入力して下さい`,
```

という記載ができるのですが、`path`の値はそのままだと変数名ですので、`label()`関数を使って

```js
Yup.object().shape({
  zipcode: Yup.string().label('郵便番号').length(7),
});
```

みたいな書き方が必要です。カスタマイズファイルの中で`path=>label`の変換なんかをしてみても面白そうです。

#独自メソッドの追加
`addMethod()`と`test()`関数を使います。

```js
Yup.addMethod(Yup.string, 'katakana', function() {
  return this.test('katakana', 'カタカナで入力して下さい', function(value) {
    if (value == null || value === '') return true;
    return value.match(/^[ァ-ヶー　 ]+$/);
  });
});
Yup.addMethod(Yup.string, 'unique', function(api) {
  return this.test('unique', '既に使用されています', function(value) {
    if (value == null || value === '') return true;
    return new Promise(resolve => {
      fetch(api + value).then(response => resolve(response.text === 'true'));
    });
  });
});
```

使うときは

```js
Yup.string().katakana();
Yup.string().unique(apiPath);
```

のようになります。`arrow function`を使うと`this`を上書きしてしまいますので

- this.path: 評価中の変数名
- this.from: Yupオブジェクトがネストされている場合、親の情報が取得できる
- this.createError(): 独自にエラーメッセージの作成ができる

等を利用したい場合は `()=>{}`では無く`function(){}`を使いましょう。
`addMethod()`で追加した関数にもsetLocaleが使えれば良かったのですが、駄目みたいです。

#変換について
[このへん](https://github.com/jaredpalmer/formik/pull/728)でも議論になっていますが、Formik で使う際に、`Yup.cast()` や、 `Yup.tranform()`による変換は基本的に動作しません。 自分は`onSubmit`の時に独自に変換していますが、どうしても使いたければ [formik-yup](https://github.com/vleandersson/formik-yup)を使いましょう。

#その他
サンプルで書いた unique() なんかは`<Formik />`中でで利用していると、検証の対象外の入力フォームを操作していても動作しているみたいです。このあたり情報をお持ちでしたら誰か教えて下さい。