Formik is a library that makes form handling in React apps easy.
In this article, we’ll look at how to handle form inputs with Formik
Dependent Fields
We can set one field’s value based on a value of another field.
For instance, we can write:
import React from "react";
import { Formik, Field, Form, useField, useFormikContext } from "formik";
const MyField = (props) => {
const {
values: { textA },
touched,
setFieldValue
} = useFormikContext();
const [field, meta] = useField(props);
React.useEffect(() => {
if (textA.trim() !== "" && touched.textA) {
setFieldValue(props.name, `textA: ${textA}`);
}
}, [textA, touched.textA, setFieldValue, props.name]);
return (
<>
<input {...props} {...field} />
{!!meta.touched && !!meta.error && <div>{meta.error}</div>}
</>
);
};
const initialValues = { textA: "", textB: "" };
export const FormExample = () => (
<div>
<Formik
initialValues={initialValues}
onSubmit={async (values) => alert(JSON.stringify(values, null, 2))}
>
<Form>
<label>
textA
<Field name="textA" />
</label>
<label>
textB
<MyField name="textB" />
</label>
<button type="submit">Submit</button>
</Form>
</Formik>
</div>
);
export default function App() {
return (
<div className="App">
<FormExample />
</div>
);
}
We created the MyField
component and use the useFormikContext
component to get the status of the textA
field.
We get its value from the values.textA
property.
touched
has the touched value.
setFieldValue
lets us set the field value of another field.
We watch the value the textA
field with the useEffect
callback and the array with the textA
field value, the touched.textA
property which has the touched status of textA
, setFieldValue
, and props.name
.
We watch all of them for changes.
Then in FormExample
, we use MyField
to create the textB
field.
It watches the textA
field for changes, so when we enter something into the textA
field, we see the text of the textB
set with the value of textA
.
Object Array Fields
We can bind fields to an array of objects.
To do this, we write:
import React from "react";
import { Formik, Field, Form, FieldArray, ErrorMessage } from "formik";
const initialValues = {
friends: [
{
name: "",
email: ""
}
]
};
export const FormExample = () => (
<div>
<Formik
initialValues={initialValues}
onSubmit={async (values) => {
await new Promise((r) => setTimeout(r, 500));
alert(JSON.stringify(values, null, 2));
}}
>
{({ values }) => (
<Form>
<FieldArray name="friends">
{({ remove, push }) => (
<div>
{values.friends.length > 0 &&
values.friends.map((friend, index) => (
<div className="row" key={index}>
<div className="col">
<label htmlFor={`friends.${index}.name`}>Name</label>
<Field name={`friends.${index}.name`} type="text" />
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
</div>
<div className="col">
<label htmlFor={`friends.${index}.email`}>Email</label>
<Field name={`friends.${index}.email`} type="email" />
<ErrorMessage
name={`friends.${index}.name`}
component="div"
className="field-error"
/>
</div>
<div className="col">
<button
type="button"
className="secondary"
onClick={() => remove(index)}
>
X
</button>
</div>
</div>
))}
<button
type="button"
className="secondary"
onClick={() => push({ name: "", email: "" })}
>
Add
</button>
</div>
)}
</FieldArray>
<button type="submit">Invite</button>
</Form>
)}
</Formik>
</div>
);
export default function App() {
return (
<div className="App">
<FormExample />
</div>
);
}
We just set the name
to the path of the property we want to bind to in the Field
component.
Likewise, we do the same with the ErrorMessage
field to get the form validation error messages for the field with the given property path.
To remove an entry, we can use the remove
function that comes with Formik.
Conclusion
We can add dependant fields and bind fields to an array of objects with Formik.