Formik（というかYup）でemailがユニークかどうかとかpasswordが一致するかなどを検証する

## 前提

ReactNativeでの話です。あと、[この記事](https://qiita.com/zaburo/items/f7908237936881305faa)の続き的なものです。Formikの基本的な動作はそちらを御覧ください。


## やりたいこと

* emailが既に登録されていないか検証
* passwordとpasswordConfirm（確認用）が一致するかどうか検証

下記のような感じ。

<img width="584" alt="スクリーンショット 2018-11-25 7.06.25.png" src="https://qiita-image-store.s3.amazonaws.com/0/55188/57664eae-fe64-ce2d-55dc-4c65d2b4a4ea.png">


## やりかた

### emailが既に存在するかどうか

.test()を利用してみます。.test()はカスタムバリデーションの実装に利用できる関数で、

```
.test([テスト名],[エラーメッセージ],[関数])
```

という形式で、引数で与えられたvalueを検証し、true, falseを返すことでカスタムバリデーションを実装できるようです。

### passwordが一致するかどうか

Yupでは、

```
.oneOf()
```

という形式で他の値を参照できるので利用してみます。

## 実装

```diff:App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Formik, yupToFormErrors } from 'formik';
import { Card, Button, FormLabel, FormInput, FormValidationMessage } from 'react-native-elements';
import * as Yup from 'yup';

export default class App extends React.Component {
  render() {
    return (
      <View style={{ paddingVertical: 60 }}>
        <Formik
          initialValues={{
            name: '',
            email: '',
            password: '',
            passwordConfirm: ''
          }}
          onSubmit={values => this._handleSubmit(values)}
          validationSchema={Yup.object().shape({
            name: Yup
              .string()
              .required('名前は必須です。'),
            email: Yup
              .string()
              .email('形式がemailではありません。')
              .required('emailは必須です。')
+             .test('email-test', '既に存在します。', (value) => {
+               if (value === 'test@test.com') {
+                 return false;
+               } else {
+                 return true;
+               }
+             }),
            password: Yup
              .string()
              .min(4, 'パスワードは最低4文字です。')
              .required('passwordは必須です。'),
            passwordConfirm: Yup
              .string()
+             .oneOf([Yup.ref('password')], 'passwordが一致しません。')
              .required('passwordは必須です。'),
          })}
        >
          {
            ({ handleChange, handleSubmit, values, errors }) => (
              <Card title='登録フォーム'>
                <FormLabel>Name</FormLabel>
                <FormInput
                  autoCapitalize='none'
                  onChangeText={handleChange('name')}
                  value={values.name}
                />
                <FormValidationMessage>{errors.name}</FormValidationMessage>

                <FormLabel>Email</FormLabel>
                <FormInput
                  autoCapitalize='none'
                  onChangeText={handleChange('email')}
                  value={values.email}
                />
                <FormValidationMessage>{errors.email}</FormValidationMessage>

                <FormLabel>Password</FormLabel>
                <FormInput
                  autoCapitalize='none'
                  onChangeText={handleChange('password')}
                  value={values.password}
                  secureTextEntry
                />
                <FormValidationMessage>{errors.password}</FormValidationMessage>

                <FormLabel>PasswordConfirm</FormLabel>
                <FormInput
                  autoCapitalize='none'
                  onChangeText={handleChange('passwordConfirm')}
                  value={values.passwordConfirm}
                  secureTextEntry
                />
                <FormValidationMessage>{errors.passwordConfirm}</FormValidationMessage>

                <Button
                  title="登録"
                  onPress={handleSubmit}
                  buttonStyle={{ marginTop: 20 }}
                />
              </Card>
            )
          }
        </Formik>
      </View>
    );
  }

  _handleSubmit = (values) => {
    alert(JSON.stringify(values));
  }
}

```

上記では、emailは単にtest@test.comに一致するかどうかで検証していますが、実際にはaxios等でAPIを照会することになります。