Open10

formik を使う前に、知っておきたかった点をまとめてみた

naokinaoki

サンプル

ミニマムのサンプル。

import React from 'react';
import { useFormik } from 'formik';

const SignupForm = () => {
  // Pass the useFormik() hook initial form values and a submit function that will
  // be called when the form is submitted
  const formik = useFormik({
    initialValues: {
      email: '',
    },
    onSubmit: values => {
      alert(JSON.stringify(values, null, 2));
    },
  });
  return (
    <form onSubmit={formik.handleSubmit}>
      <label htmlFor="email">Email Address</label>
      <input
        id="email"
        name="email"
        type="email"
        onChange={formik.handleChange}
        value={formik.values.email}
      />

      <button type="submit">Submit</button>
    </form>
  );
};
naokinaoki

初期化

useFormik に初期値や送信用の関数、バリデーションの関数を渡す。

useFormik({
  initialValues:  { name: "" } // 初期値
  onSubmit: () => {}, // 送信用の関数
  validate: (values) => {} // バリデーション用の関数
})
naokinaoki

インプット要素に指定するメソッド

  • formik.handleChange
    • フォームの値を更新するための関数。(フォームを自作した人は分かると思うが、送信ボタンをクリックする前に、フォームの値を更新するための state 管理をやってくれる)
  • formik.handleBlur
    • 指定しておくとフォームが入力されたフラグ(formik.touched ) を使えるようになる。エラーメッセージを出す時に {(formik.touched.firstName && formik.errors.firstName) && ( <div>{formik.errors.firstName}</div>} と指定しておくと入力されたフォームのみエラーメッセージを出せる。
       <input
         id="firstName"
         name="firstName"
         type="text"
         onChange={formik.handleChange}
         onBlur={formik.handleBlur}
         value={formik.values.firstName}
       />
       {formik.touched.firstName && formik.errors.firstName ? (
         <div>{formik.errors.firstName}</div>
       ) : null}
naokinaoki

バリデーションライブラリ Yup

ライブラリ Yup を使うと、useFormikvalidationSchema を渡して、以下のようにバリデーションを書くことができる

   const formik = useFormik({
     validationSchema: Yup.object({
       name: Yup.string()
         .max(15, 'Must be 15 characters or less')
         .required('Required'),
       email: Yup.string().email('Invalid email address').required('Required'),
     }),
   });
naokinaoki

getFieldProps で簡潔に書く

formik.getFieldProps('インプット要素のID') を指定すると、onChange, onBlur, value, name を指定する必要がなくなる。

       <input
         id="firstName"
         type="text"
         {...formik.getFieldProps('firstName')}
       />
naokinaoki

コンポーネント形式で書く

useFormik を、<Formik/> コンポーネントに置き換えても書ける

import React from 'react';
import { Formik } from 'formik';
import * as Yup from 'yup';

const SignupForm = () => {
  return (
    <Formik
      initialValues={{ firstName: '', lastName: '', email: '' }}
      validationSchema={Yup.object({
        firstName: Yup.string()
          .max(15, 'Must be 15 characters or less')
          .required('Required'),
        lastName: Yup.string()
          .max(20, 'Must be 20 characters or less')
          .required('Required'),
        email: Yup.string().email('Invalid email address').required('Required'),
      })}
      onSubmit={(values, { setSubmitting }) => {
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
          setSubmitting(false);
        }, 400);
      }}
    >
      {formik => (
        <form onSubmit={formik.handleSubmit}>
          <label htmlFor="firstName">First Name</label>
          <input
            id="firstName"
            type="text"
            {...formik.getFieldProps('firstName')}
          />
          {formik.touched.firstName && formik.errors.firstName ? (
            <div>{formik.errors.firstName}</div>
          ) : null}

          <label htmlFor="lastName">Last Name</label>
          <input
            id="lastName"
            type="text"
            {...formik.getFieldProps('lastName')}
          />
          {formik.touched.lastName && formik.errors.lastName ? (
            <div>{formik.errors.lastName}</div>
          ) : null}

          <label htmlFor="email">Email Address</label>
          <input id="email" type="email" {...formik.getFieldProps('email')} />
          {formik.touched.email && formik.errors.email ? (
            <div>{formik.errors.email}</div>
          ) : null}

          <button type="submit">Submit</button>
        </form>
      )}
    </Formik>
  );
};
naokinaoki

Material UI Kit + Formik + Yup を使う

Yup の日本語化は以下のように実装する

Yup.string().max(2, '2文字以内で入力してください').required('必須項目です')

MUI と formik を合わせて使うには以下のように実装する

      <TextField
        id='title'
        type={'text'}
        label="タイトル"
        {...formik.getFieldProps('title')}
        error={Boolean(formik.touched.title && formik.errors.title)}
        helperText={formik.touched.title && formik.errors.title ? formik.errors.title : null}
      />

フルサンプル

const formik = useFormik({
  initialValues: {
    title: '',
    submit: null
  },
  validationSchema: Yup.object({
    title: Yup.string().max(2, '2文字以内で入力してください').required('必須項目です'),
  }),
  onSubmit: async (values, helpers) => {
    console.log(values)
  }
});
return (
  <>
    <form
      onSubmit={formik.handleSubmit}>
      <TextField
        id='title'
        type={'text'}
        label="タイトル"
        {...formik.getFieldProps('title')}
        error={Boolean(formik.touched.title && formik.errors.title)}
        helperText={formik.touched.title && formik.errors.title ? formik.errors.title : null}
      />
      <Button type='submit'>
        保存
      </Button>
    </form>
  </>
);
naokinaoki

formik を MUI のセレクトコンポーネントに組み込む

<FormControl fullWidth>
<InputLabel id="result-category">カテゴリ</InputLabel>
<Select
  labelId="result-category"
  id='category'
  label="カテゴリ"
  {...formik.getFieldProps('category')}
  error={Boolean(formik.touched.category && formik.errors.category)}
>
  {categoryLabelList.map((categoryLabel) => {
    return (
      <MenuItem
        key={categoryLabel.id}
        value={categoryLabel.id}
      >
        {categoryLabel.name}
      </MenuItem>
    )
  })}
</Select>
<FormHelperText>{formik.touched.category && formik.errors.category ? formik.errors.category : null}</FormHelperText>
</FormControl>
naokinaoki

Fileをアップロードする
https://zenn.dev/msksgm/articles/20211112-react-formik-image-uploader

<>
  <Button
    variant="contained"
    onClick={handleUploadClick}
  >
    画像をアップロード
  </Button>
  <input
    ref={inputFileRef}
    accept="image/*"
    style={{ display: 'none' }}
    id="avatar"
    name="avatar"
    type="file"
    onChange={(event) => {
      formik.setFieldValue(
        "avatar",
        event.target.files !== null
          ? event.target.files[0]
          : null
      );
    }}
  />
</>