Posted at

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

// @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のバリデーションで、ラベルを日本語にするのにハマった。

こんな感じで、日本語ラベルを設定できる。

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を日本語訳する必要がある。↓


// @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"
}
};