react-formik
YouTube チャンネル CodeEvolutionの「React Formik」を参考に学習しましたので共有します。
set up
terminal
$ npm i formik
useFormik を使って form 実装 Basic 編
useFormik で実装すること
- form の各 value の値を管理
- handling submit
- validate
formのHTML
<div>
<form>
<label htmlFor="name">Name</label>
<input type="text" id="name" name="name" />
<label htmlFor="email">E-mail</label>
<input type="email" id="email" name="email" />
<label htmlFor="channel">Channel</label>
<input type="text" id="channel" name="channel" />
<button>Submit</button>
</form>
</div>
form の各 value の値を管理
- useFormik 関数を変数(formik)で管理
- formik.values で各要素の value を管理
- useFormik 関数の object に
initialValuesを設定して、各 form 要素の初期値を管理 - initialValues の key 名は form 要素の
nameの値 - onChage 属性に useFormik の API
handleChangeを指定。onChange={formik.handleChange}でformik.valuesで設定した name を key としたデータが入る - value 属性に useFormik の API
valuesを指定。value={formik.values.{name名}}でformik.valuesで設定した name のデータが表示される
import { useFormik } from 'formik';
const formik = useFormik({
initialValues: {
name: '',
email: '',
channel: '',
},
});
<input
type="text"
id="name"
name="name"
onChange={formik.handleChange}
value={formik.values.name}
/>
Sample.js
import React from 'react';
import { useFormik } from 'formik';
function YoutubeForm() {
const formik = useFormik({
initialValues: {
name: '',
email: '',
channel: '',
},
});
console.log('form valuse', formik.values);
return (
<div>
<form>
<label htmlFor="name">Name</label>
<input
type="text"
id="name"
name="name"
onChange={formik.handleChange}
value={formik.values.name}
/>
<label htmlFor="email">E-mail</label>
<input
type="email"
id="email"
name="email"
onChange={formik.handleChange}
value={formik.values.email}
/>
<label htmlFor="channel">Channel</label>
<input
type="text"
id="channel"
name="channel"
onChange={formik.handleChange}
value={formik.values.channel}
/>
<button>Submit</button>
</form>
</div>
);
}
export default YoutubeForm;
submit ボタンを押下して form data を取得
- form タグの
onSubmit属性に useFormik の APIhandleSubmitを指定。onSubmit={formik.handleSubmit} - button タグの type を
submitにする -
useFormik関数の object にonSubmitメソッドを記述して form の各要素の value を取得
const formik = useFormik({
onSubmit: (values) => {
console.log('form data', values);
},
});
<form onSubmit={formik.handleSubmit}>
<button type="submit">Submit</button>
Sample.js
import React from 'react';
import { useFormik } from 'formik';
function YoutubeForm() {
const formik = useFormik({
initialValues: {
name: '',
email: '',
channel: '',
},
onSubmit: (values) => {
console.log('form data', values);
},
});
return (
<div>
<form onSubmit={formik.handleSubmit}>
<label htmlFor="name">Name</label>
<input
type="text"
id="name"
name="name"
onChange={formik.handleChange}
value={formik.values.name}
/>
<label htmlFor="email">E-mail</label>
<input
type="email"
id="email"
name="email"
onChange={formik.handleChange}
value={formik.values.email}
/>
<label htmlFor="channel">Channel</label>
<input
type="text"
id="channel"
name="channel"
onChange={formik.handleChange}
value={formik.values.channel}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default YoutubeForm;
validate 実装
- useFormik 関数に validate メソッドを指定
- validate メソッドには
errorsを戻り値とする - validate メソッドの引数
valuesには各要素の name の値を key とした value が入る -
formik.errorsで各要素のエラーメッセージを取得
const formik = useFormik({
validate: (values) => {
let errors = {};
if (!values.name) {
errors.name = 'Name is Required';
}
if (!values.email) {
errors.email = 'Email is Required';
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email format';
}
if (!values.channel) {
errors.channel = 'Channel is Required';
}
return errors;
}
});
{formik.errors.name ? (
<div className="error">{formik.errors.name}</div>
) : null}
Sample.js
import React from 'react';
import { useFormik } from 'formik';
const initialValues = {
name: '',
email: '',
channel: '',
};
const onSubmit = (values) => {
console.log('form data', values);
};
const validate = (values) => {
let errors = {};
if (!values.name) {
errors.name = 'Name is Required';
}
if (!values.email) {
errors.email = 'Email is Required';
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email format';
}
if (!values.channel) {
errors.channel = 'Channel is Required';
}
return errors;
};
function YoutubeForm() {
const formik = useFormik({
initialValues,
onSubmit,
validate,
});
return (
<div>
<form onSubmit={formik.handleSubmit}>
<div className="form-control">
<label htmlFor="name">Name</label>
<input
type="text"
id="name"
name="name"
onChange={formik.handleChange}
value={formik.values.name}
/>
{formik.errors.name ? (
<div className="error">{formik.errors.name}</div>
) : null}
</div>
<div className="form-control">
<label htmlFor="email">E-mail</label>
<input
type="email"
id="email"
name="email"
onChange={formik.handleChange}
value={formik.values.email}
/>
{formik.errors.email ? (
<div className="error">{formik.errors.email}</div>
) : null}
</div>
<div className="form-control">
<label htmlFor="channel">Channel</label>
<input
type="text"
id="channel"
name="channel"
onChange={formik.handleChange}
value={formik.values.channel}
/>
{formik.errors.channel ? (
<div className="error">{formik.errors.channel}</div>
) : null}
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default YoutubeForm;
validate 選択項目のみエラーメッセージを表示
- ユーザーが touch した項目のみ validate を検知
- handleBlur したときにエラーメッセージ表示
<input
onBlur={formik.handleBlur}
/>
{formik.touched.name && formik.errors.name ? (
<div className="error">{formik.errors.name}</div>
) : null}
Sample.js
import React from 'react';
import { useFormik } from 'formik';
const initialValues = {
name: '',
email: '',
channel: '',
};
const onSubmit = (values) => {
console.log('form data', values);
};
const validate = (values) => {
let errors = {};
if (!values.name) {
errors.name = 'Name is Required';
}
if (!values.email) {
errors.email = 'Email is Required';
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email format';
}
if (!values.channel) {
errors.channel = 'Channel is Required';
}
return errors;
};
function YoutubeForm() {
const formik = useFormik({
initialValues,
onSubmit,
validate,
});
return (
<div>
<form onSubmit={formik.handleSubmit}>
<div className="form-control">
<label htmlFor="name">Name</label>
<input
type="text"
id="name"
name="name"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.name}
/>
{formik.touched.name && formik.errors.name ? (
<div className="error">{formik.errors.name}</div>
) : null}
</div>
<div className="form-control">
<label htmlFor="email">E-mail</label>
<input
type="email"
id="email"
name="email"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.email}
/>
{formik.touched.email && formik.errors.email ? (
<div className="error">{formik.errors.email}</div>
) : null}
</div>
<div className="form-control">
<label htmlFor="channel">Channel</label>
<input
type="text"
id="channel"
name="channel"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.channel}
/>
{formik.touched.channel && formik.errors.channel ? (
<div className="error">{formik.errors.channel}</div>
) : null}
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default YoutubeForm;
useFormik を使って form 実装 Advanced 編
yup を install
- formik と連携してバリデーションチェックをする package
- Yup.object に
validation schemaを定義 - useFormik 関数の object に指定
$ npm i yup
import * as Yup from 'yup';
const validationSchema = Yup.object({
name: Yup.string().required('Required'),
email: Yup.string().email('Invalid email format').required('Required'),
channel: Yup.string().required('Required'),
});
const formik = useFormik({
initialValues,
onSubmit,
validationSchema,
});
Sample.js
import * as Yup from 'yup';
import React from 'react';
import { useFormik } from 'formik';
const initialValues = {
name: '',
email: '',
channel: '',
};
const onSubmit = (values) => {
console.log('form data', values);
};
const validationSchema = Yup.object({
name: Yup.string().required('Required'),
email: Yup.string().email('Invalid email format').required('Required'),
channel: Yup.string().required('Required'),
});
function YoutubeForm() {
const formik = useFormik({
initialValues,
onSubmit,
validationSchema,
});
return (
<div>
<form onSubmit={formik.handleSubmit}>
<div className="form-control">
<label htmlFor="name">Name</label>
<input
type="text"
id="name"
name="name"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.name}
/>
{formik.touched.name && formik.errors.name ? (
<div className="error">{formik.errors.name}</div>
) : null}
</div>
<div className="form-control">
<label htmlFor="email">E-mail</label>
<input
type="email"
id="email"
name="email"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.email}
/>
{formik.touched.email && formik.errors.email ? (
<div className="error">{formik.errors.email}</div>
) : null}
</div>
<div className="form-control">
<label htmlFor="channel">Channel</label>
<input
type="text"
id="channel"
name="channel"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.channel}
/>
{formik.touched.channel && formik.errors.channel ? (
<div className="error">{formik.errors.channel}</div>
) : null}
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default YoutubeForm;
formik.getFieldProps({name 属性の値})で refactor
-
onChange属性、onBlur属性、value属性の指定を getFieldProps メソッドでリファクタリングする - name 属性で指定した値を引数に設定
- スプレッド構文で展開
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.name}
↓
{...formik.getFieldProps('name')}
Sample.js
import * as Yup from 'yup';
import React from 'react';
import { useFormik } from 'formik';
const initialValues = {
name: '',
email: '',
channel: '',
};
const onSubmit = (values) => {
console.log('form data', values);
};
const validationSchema = Yup.object({
name: Yup.string().required('Required'),
email: Yup.string().email('Invalid email format').required('Required'),
channel: Yup.string().required('Required'),
});
function YoutubeForm() {
const formik = useFormik({
initialValues,
onSubmit,
validationSchema,
});
return (
<div>
<form onSubmit={formik.handleSubmit}>
<div className="form-control">
<label htmlFor="name">Name</label>
<input
type="text"
id="name"
name="name"
{...formik.getFieldProps('name')}
/>
{formik.touched.name && formik.errors.name ? (
<div className="error">{formik.errors.name}</div>
) : null}
</div>
<div className="form-control">
<label htmlFor="email">E-mail</label>
<input
type="email"
id="email"
name="email"
{...formik.getFieldProps('email')}
/>
{formik.touched.email && formik.errors.email ? (
<div className="error">{formik.errors.email}</div>
) : null}
</div>
<div className="form-control">
<label htmlFor="channel">Channel</label>
<input
type="text"
id="channel"
name="channel"
{...formik.getFieldProps('channel')}
/>
{formik.touched.channel && formik.errors.channel ? (
<div className="error">{formik.errors.channel}</div>
) : null}
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default YoutubeForm;
Formik コンポーネントで全体をラップ
- formik が提供している
Formikコンポーネントで form タグを wrap -
Formikコンポーネントの propsinitialValues、validationSchema、onSubmitにそれぞれの変数を指定
import { Formik } from 'formik';
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
>
form タグを Form コンポーネントに変更
- formik が提供している
Formコンポーネントを form タグと変更 - form タグで指定した onSubmit 属性を削除
import { Form } from 'formik';
<form onSubmit={formik.handleSubmit}>
↓
<Form>
input タグを Field コンポーネントに変更
- formik が提供している
Fieldコンポーネントを input タグと変更 -
{...formik.getFieldProps()}を削除
import { Field } from 'formik';
<input type="text" id="name" name="name" {...formik.getFieldProps('name')} />
↓
<Field type="text" id="name" name="name" />
ErrorMessage コンポーネントでエラーメッセージ表示
- formik が提供している
ErrorMessageコンポーネントでエラーメッセージ表示
import { ErrorMessage } from 'formik';
{formik.touched.name && formik.errors.name ? (
<div className="error">{formik.errors.name}</div>
) : null}
↓
<ErrorMessage name="name" />
Sample.js
import * as Yup from 'yup';
import { ErrorMessage, Field, Form, Formik } from 'formik';
import React from 'react';
const initialValues = {
name: '',
email: '',
channel: '',
};
const onSubmit = (values) => {
console.log('form data', values);
};
const validationSchema = Yup.object({
name: Yup.string().required('Required'),
email: Yup.string().email('Invalid email format').required('Required'),
channel: Yup.string().required('Required'),
});
function YoutubeForm() {
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
>
<Form>
<div className="form-control">
<label htmlFor="name">Name</label>
<Field type="text" id="name" name="name" />
<ErrorMessage name="name" />
</div>
<div className="form-control">
<label htmlFor="email">E-mail</label>
<Field type="email" id="email" name="email" />
<ErrorMessage name="email" />
</div>
<div className="form-control">
<label htmlFor="channel">Channel</label>
<Field type="text" id="channel" name="channel" />
<ErrorMessage name="channel" />
</div>
<button type="submit">Submit</button>
</Form>
</Formik>
);
}
export default YoutubeForm;
Field コンポーネントについて
-
placeholderprops で placeholder を入力できる -
asprops で textarea、select タグを設定できる -
fieldコンポーネントの children に関数が書ける。その関数の引数にfield、form、metaが設定される
<Field placeholder="YouTube channel name" />
<Field as="textarea" id="comments" name="comments" />
<Field name="address">
{({ field, form, meta }) => {
return (
<div>
<input type="text" {...field} />
{meta.touched && meta.error ? <div>{meta.error}</div> : null}
</div>
);
}}
</Field>
ErrorMessage コンポーネントについて
- ErrorMessage コンポーネントの
componentprops に作成した component を指定できる - そのとき作成した component の
props.childrenに error message が表示される - ErrorMessage コンポーネントの children に関数を設定できる
- その関数の引数には error message が設定される
function TextError(props) {
return <div className="error">{props.children}</div>;
}
<ErrorMessage name="name" component={TextError} />
<ErrorMessage name="email">
{(msg) => <div className="error">{msg}</div>}
</ErrorMessage>
value に nest された object を 指定
-
initialValuesで nest された object を指定 -
Fieldコンポーネントの name 属性で object の key を指定
const initialValues = {
social: {
facebook: '',
twitter: '',
},
};
<Field type="text" id="twitter" name="social.twitter" />
value に配列を指定
-
initialValuesで value に配列を指定 -
Fieldコンポーネントの name 属性で 配列の key と配列番号 を指定
const initialValues = {
phoneNumbers: ['', ''],
};
<Field type="text" id="primaryPh" name="phoneNumbers[0]" />
<Field type="text" id="secondaryPh" name="phoneNumbers[1]" />
Field Array コンポーネントで動的に input タグを増減させる
- formik が提供している
FieldArrayコンポーネントを使用 -
initialValuesで配列を value とした key を設定 -
FieldArrayコンポーネントの name 属性にinitialValuesで設定した key を指定 -
FieldArrayコンポーネントのchildrenに関数を設定 - 関数の引数には
push、remove、formなどの API が設定されている-
formには value の key があり、initialValuesで設定した key が管理されている - その key を map させて、
Fieldコンポーネントとbuttonを配置させる -
Fieldコンポーネントの name 属性はinitialValuesで設定した key と map の引数(index)を結合させる - map の引数(index)を使用して
remove(index)で input タグの削除を行う - 先頭の input タグは削除しないように設定する
- button タグの onClick 属性に
push('')を設定で input タグを増やす
-
import { FieldArray } from 'formik';
const initialValues = {
phNumbers: [''],
};
<FieldArray name="phNumbers">
{(fieldArrayProps) => {
const { push, remove, form } = fieldArrayProps;
const {
values: { phNumbers },
} = form;
return (
<div>
{phNumbers.map((phNumber, index) => (
<div key={index}>
<Field name={`phNumbers[${index}]`} />
{index > 0 && (
<button type="button" onClick={() => remove(index)}>
-
</button>
)}
</div>
))}
<button type="button" onClick={() => push('')}>
+
</button>
</div>
);
}}
</FieldArray>
FastField コンポーネントでレンダリングを制御
-
Fieldコンポーネントを操作したとき、他のFieldコンポーネントもレンダリングされる - formik で提供されている
FastFieldコンポーネントを使用すると、そのコンポーネントのみレンダリングされる - 1 画面に 30 以上の
Fieldコンポーネントがあある場合にFastFieldの使用でパフォーマンスが良くなる
import { FastField } from 'formik';
<FastField name="address">
{({ field, form, meta }) => {
console.log('Field render');
return (
<div>
<input type="text" {...field} />
{meta.touched && meta.error ? <div>{meta.error}</div> : null}
</div>
);
}}
</FastField>
validation が走るタイミングを制御
- デフォルトでは form の各項目を
onChange、onBlurしたタイミングで validation が走る -
Formikコンポーネントの props でvalidateOnChange、validateOnBlurをfalseにすると validation が走らないようになる
<Formik
validateOnChange={false}
validateOnBlur={false}
>
Field コンポーネントで validation を定義
-
validationSchemaで validation を制御するのではなく、Fieldコンポーネントのvalidateprops で validation を制御 -
vilidateprops には関数が指定でき、引数にはvalueが入る - formik が提供している
ErrorMessageコンポーネントでエラーメッセージを表示
const initialValues = {
comments: '',
};
const validateComments = (value) => {
let error;
if (!value) {
error = 'Required';
}
return error;
};
<Field validate={validateComments} />
<ErrorMessage name="comments" />
trigger validation
-
Formikコンポーネントの children に関数を設定できて、その関数の引数にはヘルパーメソッドが定義されている -
formik.validateField({name属性の値})、formik.setFieldTouched({name属性の値})で単一の fileld の値取得と操作ができる -
formik.validateForm()、formik.setTouched({ name: true })で validation を設定した全ての値取得と操作ができる
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
>
{(formik) => {
return (
<Form>
.
.
.
</Form>
)
}}
</Formik>
<button
type="button"
onClick={() => formik.validateField('comments')}
>
Validate comments
</button>
<button
type="button"
onClick={() => formik.setFieldTouched('comments')}
>
Visit comments
</button>
<button type="button" onClick={() => formik.validateForm()}>
Validate all
</button>
<button
type="button"
onClick={() =>
formik.setTouched({
name: true,
email: true,
channel: true,
comments: true,
})
}
>
Visit all
</button>
submit ボタンを disabled で制御
-
formik.isValidで validation エラーの有無を boolean で取得。trueの場合はエラーなし-
formik.isValidのみで disabled を制御すると page ロード時はエラーがないのでボタンが活性化される
-
-
formik.dirtyでinitialValuesと値が同じかをチェック- required の値が
initialValues時に入ってる場合、formik.dirtyで値と同じではないとfalseになる。よって disabled は制御できない
- required の値が
-
formik.isSubmittingで submit ボタンが押されたかを判定(trueの場合は押下)。この判定により API にデータを POST している非同期処理中はfalseに設定して、submit ボタンを非活性化にする-
onSubmit関数の引数submitPropsのメソッドsetSubmittingでformik.isSubmittingを制御できる
-
const onSubmit = (values, submitProps) => {
submitProps.setSubmitting(false);
};
<button
type="submit"
disabled={!formik.isValid || formik.isSubmitting}
>
この記述でページ初期画面はボタンが活性化になっているが、押下するとエラーメッセージが表示され、onSubmit 関数が処理されない
load data で initialValues の値を変更する
- mock として
initialValuesと同じ schema のsavedValuesを用意 - button を押下したときに useState で
savedValuesで設定した値に変更 -
Formikの propsinitialValuesでsavedValuesまたはinitialValuesの値になるようにする -
Formikの props でenableReinitializeでinitialValuesが変更可能に
const savedValues = {
name: 'Vishwas',
email: 'v@example.com',
channel: 'codevolution',
comments: 'Welcome to Formik',
address: '221B Baker Street',
social: {
facebook: '',
twitter: '',
},
phoneNumbers: ['', ''],
phNumbers: [''],
};
const [formValues, setFormValues] = useState(null);
<button type="button" onClick={() => setFormValues(savedValues)}>
Load saved data
</button>
<Formik
initialValues={formValues || initialValues}
enableReinitialize
>
Form の値を reset させる
- button を押下して reset させるには
type="reset"を設定する - submit ボタンを押下した後に reset させるには
onSubmit関数の引数submitPropsのメソッドを使用submitProps.resetForm()
<button type='reset'>Reset</button>
const onSubmit = (values, submitProps) => {
submitProps.resetForm();
};
Sample.jsの最終形
Sample.js
import * as Yup from 'yup';
import {
ErrorMessage,
FastField,
Field,
FieldArray,
Form,
Formik,
} from 'formik';
import React, { useState } from 'react';
import TextError from './TextError';
const initialValues = {
name: '',
email: '',
channel: '',
comments: '',
address: '',
social: {
facebook: '',
twitter: '',
},
phoneNumbers: ['', ''],
phNumbers: [''],
};
const savedValues = {
name: 'Vishwas',
email: 'v@example.com',
channel: 'codevolution',
comments: 'Welcome to Formik',
address: '221B Baker Street',
social: {
facebook: '',
twitter: '',
},
phoneNumbers: ['', ''],
phNumbers: [''],
};
const onSubmit = (values, submitProps) => {
console.log('form data', values);
console.log('submitProps', submitProps);
submitProps.setSubmitting(false);
submitProps.resetForm();
};
const validationSchema = Yup.object({
name: Yup.string().required('Required'),
email: Yup.string().email('Invalid email format').required('Required'),
channel: Yup.string().required('Required'),
});
const validateComments = (value) => {
let error;
if (!value) {
error = 'Required';
}
return error;
};
function YoutubeForm() {
const [formValues, setFormValues] = useState(null);
return (
<Formik
initialValues={formValues || initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
enableReinitialize
// validateOnMount
// validateOnChange={false}
// validateOnBlur={false}
>
{(formik) => {
console.log('Formik props', formik);
return (
<Form>
<div className="form-control">
<label htmlFor="name">Name</label>
<Field type="text" id="name" name="name" />
<ErrorMessage name="name" component={TextError} />
</div>
<div className="form-control">
<label htmlFor="email">E-mail</label>
<Field type="email" id="email" name="email" />
<ErrorMessage name="email">
{(error) => <div className="error">{error}</div>}
</ErrorMessage>
</div>
<div className="form-control">
<label htmlFor="channel">Channel</label>
<Field
type="text"
id="channel"
name="channel"
placeholder="YouTube channel name"
/>
<ErrorMessage name="channel" />
</div>
<div className="form-control">
<label htmlFor="comments">Comments</label>
<Field
as="textarea"
id="comments"
name="comments"
validate={validateComments}
/>
<ErrorMessage name="comments" component={TextError} />
</div>
<div className="form-control">
<label htmlFor="address">Address</label>
<FastField name="address">
{({ field, form, meta }) => {
return (
<div>
<input type="text" {...field} />
{meta.touched && meta.error ? (
<div>{meta.error}</div>
) : null}
</div>
);
}}
</FastField>
</div>
<div className="form-control">
<label htmlFor="facebook">Facebook profile</label>
<Field type="text" id="facebook" name="social.facebook" />
</div>
<div className="form-control">
<label htmlFor="twitter">Twitter profile</label>
<Field type="text" id="twitter" name="social.twitter" />
</div>
<div className="form-control">
<label htmlFor="primaryPh">Primary phone number</label>
<Field type="text" id="primaryPh" name="phoneNumbers[0]" />
</div>
<div className="form-control">
<label htmlFor="secondaryPh">Secondary phone number</label>
<Field type="text" id="secondaryPh" name="phoneNumbers[1]" />
</div>
<div className="form-control">
<label>List of phone numbers</label>
<FieldArray name="phNumbers">
{(fieldArrayProps) => {
const { push, remove, form } = fieldArrayProps;
const {
values: { phNumbers },
} = form;
return (
<div>
{phNumbers.map((phNumber, index) => (
<div key={index}>
<Field name={`phNumbers[${index}]`} />
{index > 0 && (
<button type="button" onClick={() => remove(index)}>
-
</button>
)}
</div>
))}
<button type="button" onClick={() => push('')}>
+
</button>
</div>
);
}}
</FieldArray>
</div>
<button type="button" onClick={() => setFormValues(savedValues)}>
Load saved data
</button>
<button type="reset">Reset</button>
<button
type="submit"
disabled={!formik.isValid || formik.isSubmitting}
>
Submit
</button>
</Form>
);
}}
</Formik>
);
}
export default YoutubeForm;