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


前提

ReactNativeでの話です。あと、この記事の続き的なものです。Formikの基本的な動作はそちらを御覧ください。

やりたいこと

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

下記のような感じ。

やりかた

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

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

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

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

passwordが一致するかどうか

Yupでは、

.oneOf()

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

実装

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 === '[email protected]') {
+                 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は単に[email protected]に一致するかどうかで検証していますが、実際にはaxios等でAPIを照会することになります。