Formikのv2.0.1-rc.13で紹介されているuseField()
が気になったので、使ってみました。
Hooksの対応が革新的そうです!
▼ Formik Releases v2.0.1-rc.13
https://github.com/jaredpalmer/formik/releases/tag/v2.0.1-rc.13
useField()
の紹介は公式サイトを見てみてください。
Fomik 公式 useField()
導入
formikはTypescriptで書かれてるので、@typesライブラリのインストールは必要ありません!
Formik推奨のバリデーションライブラリ、Yupも入れておくことにします。
$ npm install formik yup @types/yup
それと、今回はreact-selectの<Select />
タグを使いたかったので、そちらも入れておくことにします。
react-selectはセレクトボックスをいい感じに拡張してくれるライブラリです。
$ npm install react-select @types/react-select
フォームの完成形
まずは完成したイメージからお見せします!アンケートフォームを作ってみた感じです。
シンプルなテキストエリア、セレクトボックスを置きました。「登録する」ボタンを押した時、コンソール画面に値が取得できているか確認して見ます。

実際のコード
<Formik />
フォームのコンポーネントに<Formik />
タグを設置して、初期値(initialValues
)や「登録」を押した時のonSubmit
、バリデーション(validationSchema
)を設置しています。
import { Formik, FormikProps } from "formik"
import React from "react"
import * as Yup from "yup"
export const SampleForm: React.FC = () => {
return (
<div>
<h6>アンケートフォーム</h6>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values: FormValues) => { console.log(values) }}
render={(props: FormikProps<FormValues>) => (
<form onSubmit={props.handleSubmit}>
{/* 各フォーム要素 */}
<button type="submit">登録する</button>
</form>
)}
/>
</div>
)
}
ちなみに、先ほどの完成形で設定した初期値は下の通りです。
const initialValues: FormValues = {
nickname: "",
experience: true,
message: "",
rabbits: [],
favoriteFoods: [],
petName: [],
}
今回はバリデーションに付いて触れませんが、validationSchema
の書き方は公式サイトがとても分かりやすいです! → Formik公式 Validation
<input type="text" />
ニックネームとメッセージの入力エリアは、それぞれコンポーネントを分けて作りました。
<input />
タグ用の<TextField />
コンポーネントにname
だけ渡しています。
<Formik
render={(props: FormikProps<FormValues>) => (
...
<TextField name="nickname" />
...
下が<TextField />
コンポーネントです。公式サイト通りですが、useField()
を下のように使っています。
import React from "react"
import { useField } from "formik"
type Props = {
name: string
}
export const TextField: React.FC<Props> = ({ name, ...props }) => {
const [field, meta] = useField(name)
return (
<div>
<input {...field} {...props} />
{meta.error && meta.touched && <small>{meta.error}</small>}
</div>
)
}
今回は<Field />
タグは使いませんでしたが、
Formik v2では、下のような<Field />
タグでcomponent
を指定するのは非推奨らしいです。
// 非推奨らしい
<Field name="firstName" component={CustomInputComponent} />
↓
<Field name="firstName" as={CustomInputComponent} />
react-selectを使った<Select />
<Formik
render={(props: FormikProps<FormValues>) => (
...
<RabbitSelect
name="rabbits"
options={rabbitOptions}
placeholder="1種類選択してください"
/>
...
options
で渡しているのは、label
とvalue
で値をセットした配列です。
/**
* うさぎの選択肢
*/
const rabbitOptions = [
{
label: "ネザーランド・ドワーフ",
value: "netherland-dwarf"
},
{
label: "ホーランド・ロップ",
value: "holland-lop"
},
...
]
<RabbitSelect />
コンポーネントは下のように実装しました。
setFieldValue()
も使いたかったので、useField()
と合わせてuseFormikContext()
を使っています。
(as never
は妥協です。。あまり参考になさらないでください。)
import React from "react"
import Select from "react-select"
import { ValueType } from "react-select/src/types"
import { useFormikContext, useField } from "formik"
type OptionType = {
label: string
value: string
}
type Props = {
name: string
options: OptionType[]
placeholder?: string
}
export const RabbitSelect: React.FC<Props> = ({
name,
options,
placeholder,
}) => {
const [field] = useField(name)
const { setFieldValue } = useFormikContext()
const onChange = React.useCallback((option) => {
setFieldValue(
field.name as never,
(option as OptionType).value
)
}, [field.name, setFieldValue])
const getValue = (): ValueType<OptionType> => {
if (options) {
return options.find(option => option.value === field.value)
} else {
return "" as any
}
}
return (
<Select
name={field.name}
value={getValue()}
onChange={onChange}
placeholder={placeholder}
options={options}
/>
)
}
今回はreact-selectについて触れませんが、複数選択できるのは<Select />
にisMulti
を渡すだけです!
上の<RabbitSelect />
と似たような記述です。
export const FoodSelect: React.FC<Props> = ({
... // 上と同じ
}) => {
... // 上と同じ
const onChange = React.useCallback((option) => {
setFieldValue(
field.name as never,
(option as OptionType[]).map((item) => item.value)
)
}, [field.name, setFieldValue])
const getValue = (): ValueType<OptionType> => {
if (options) {
return options.filter(option => field.value.indexOf(option.value) >= 0)
} else {
return []
}
}
return (
<Select
name={field.name}
value={getValue()}
onChange={onChange}
placeholder={placeholder}
options={options}
isMulti
/>
)
}
最後に
すみません、あまりuseField()
の詳細について触れておりません。。とりあえず実装してみたかったので今回は備忘録がてら記事にさせて頂きました!まだ試していくので、都度更新していくことにします。