React ReduxのFormのライブラリを探す


ReactでFormを作るのは、すごく面倒
React ReduxのFormライブラリを2つ試した。


redux-form
https://github.com/erikras/redux-form/issues/new

Formik
https://github.com/jaredpalmer/formik


redux-formは、onSubmit、handleSubmitの動きが気持ち悪るくて断念。

Formikは、わかりやすいが、連携するバリエーションライブラリのYupが癖がありそう。

例）　Formik + Yup + Stripe

```js 
// @flow
import React from "react";
import { CardElement, injectStripe } from "react-stripe-elements";
import "./CheckoutForm.css";
import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import { setLocale } from "yup";
import { localeJP } from "../config/yup.locale.ja";
setLocale(localeJP);
const CheckoutSchema = Yup.object().shape({
  name: Yup.string()
    .min(2)
    .max(5)
    .required()
    .label("名前"),
  email: Yup.string()
    .email()
    .required()
    .label("メールアドレス"),
  tel: Yup.string()
    .min(2)
    .max(50)
    .required()
    .label("電話番号")
});

class CheckoutForm extends React.Component {
  render() {
    const { stripe, goto, saveInputData } = this.props;
    const { name, email, tel, card } = this.props.purchase;
    return (
      <Formik
        initialValues={{
          name,
          email,
          tel
        }}
        validationSchema={CheckoutSchema}
        onSubmit={(values, { setSubmitting }) => {
          stripe.createToken().then(({ token }) => {
            if (token) {
              console.log("Received Stripe token:", token);
              console.log("onSubmit:", values);

              saveInputData({
                ...values,
                card: token
              });
              setSubmitting(false);
              goto("/account/subscriptions/confirmation");
            } else {
              console.error("createToken エラー");
            }
          });
        }}
      >
        <Form className="example example1">
          <fieldset>
            <div className="row">
              <label htmlFor="example1-name">名前</label>
              <Field
                name="name"
                type="text"
                id="example1-name"
                placeholder="Jane Doe"
                required=""
                autoComplete="name"
              />
              <ErrorMessage name="name" component="span" />
            </div>
            <div className="row">
              <label htmlFor="example1-email">メールアドレス</label>
              <Field
                name="email"
                id="example1-email"
                type="email"
                placeholder="janedoe@gmail.com"
                required=""
                autoComplete="email"
              />
              <ErrorMessage name="email" component="span" />
            </div>

            <div className="row">
              <label htmlFor="example1-phone">電話番号</label>
              <Field
                name="tel"
                id="example1-phone"
                type="tel"
                placeholder="(941) 555-0123"
                required=""
                autoComplete="tel"
              />
              <ErrorMessage name="tel" component="span" />
            </div>
          </fieldset>
          <fieldset>
            <div className="row">
              {card && <div>**** - **** - ***** - {card.card.last4}</div>}

              {!card && (
                <CardElement
                  style={{
                    base: {},
                    invalid: {}
                  }}
                />
              )}
            </div>
          </fieldset>

          <button>購入する</button>
        </Form>
      </Formik>
    );
  }
}

export default injectStripe(CheckoutForm);

```


yupのバリデーションで、ラベルを日本語にするのにハマった。
こんな感じで、日本語ラベルを設定できる。

```js
import * as Yup from "yup";
import { setLocale } from "yup";
import { localeJP } from "../config/yup.locale.ja";
setLocale(localeJP);
const CheckoutSchema = Yup.object().shape({
  name: Yup.string()
    .min(2)
    .max(5)
    .required()
    .label("名前"),
  email: Yup.string()
    .email()
    .required()
    .label("メールアドレス"),
  tel: Yup.string()
    .min(2)
    .max(50)
    .required()
    .label("電話番号")
});
```

あとは、setLocalを日本語訳する必要がある。↓

```js

// @flow
/* eslint no-template-curly-in-string:0 */

export const localeJP = {
  mixed: {
    default: "${path} is invalid",
    required: "${path} is a required field",
    oneOf: "${path} must be one of the following values: ${values}",
    notOneOf: "${path} must not be one of the following values: ${values}"
  },
  string: {
    length: "${path} must be exactly ${length} characters",
    min: "${path} must be at least ${min} characters",
    max: "${path} must be at most ${max} characters",
    matches: '${path} must match the following: "${regex}"',
    email: "${path} must be a valid email",
    url: "${path} must be a valid URL",
    trim: "${path} must be a trimmed string",
    lowercase: "${path} must be a lowercase string",
    uppercase: "${path} must be a upper case string"
  },
  number: {
    min: "${path} must be greater than or equal to ${min}",
    max: "${path} must be less than or equal to ${max}",
    lessThan: "${path} must be less than ${less}",
    moreThan: "${path} must be greater than ${more}",
    notEqual: "${path} must be not equal to ${notEqual}",
    positive: "${path} must be a positive number",
    negative: "${path} must be a negative number",
    integer: "${path} must be an integer"
  },
  date: {
    min: "${path} field must be later than ${min}",
    max: "${path} field must be at earlier than ${max}"
  },
  object: {
    noUnknown:
      "${path} field cannot have keys not specified in the object shape"
  },
  array: {
    min: "${path} field must have at least ${min} items",
    max: "${path} field must have less than or equal to ${max} items"
  }
};

```