I am using Formik to create a form on a website. In this form I have a set of tags which I want to be checkable with individual checkboxes, but keep them in an array tags
. Formik claims to be able to do so by using the same name
attribute on the Field elements, but I can not get it to work.
I am using this code example as an reference.
Here is my code:
import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field } from "formik";
import "./styles.css";
function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ProjectsForm />
</div>
);
}
const ProjectsForm = props => (
<Formik
initialValues={{
search: "",
tags: []
}}
onSubmit={values => {
console.log(values);
}}
validate={values => {
console.log(values);
}}
validateOnChange={true}
>
{({ isSubmitting, getFieldProps, handleChange, handleBlur, values }) => (
<Form>
<Field type="text" name="search" />
<label>
<Field type="checkbox" name="tags" value="one" />
One
</label>
<label>
<Field type="checkbox" name="tags" value="two" />
Two
</label>
<label>
<Field type="checkbox" name="tags" value="three" />
Three
</label>
</Form>
)}
</Formik>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Also on CodeSandbox
I am expecting to receive thought the console.log(values)
to display something like:
Object {search: "", tags: ['one', 'three']}
or
Object {search: "", tags: ['one': true, 'two': false, 'three': true]}
Hope there is a simple thing I am missing to add the Formik checkbox group functionality, as it claims to be possible.
React + Formik Required Checkbox App Component The checkbox is set to required with the schema rule acceptTerms: Yup. bool(). oneOf([true], 'Accept Terms & Conditions is required') and by setting the initial value acceptTerms: false inside initialValues .
useField Checkboxes The useField hook can take both a string name as the only property, or when you need to enable Formik to understand checkboxes pass in an object with name and the type set to checkbox. Spread the field on like normal, apply type="checkbox" and you're checkbox input is all wired up.
You can use Formik's setFieldValue function to reset the field when the checkbox is unchecked.
What you need to do is use the FormControlLabel component to handle the onChangge event that formik needs. Then on the Checkbox you just set the checked prop to be whatever formik has in its values. Here is an example I use for a Checkbox to set an isAdmin value.
It's pretty easy to accomplish using the FieldArray
component.
Place your values in an array, something like this:
const tagCollection = [
{ value: "one", label: "One" },
{ value: "two", label: "Two" },
{ value: "three", label: "Three" }
];
And then use the FieldArray
like this:
<FieldArray
name="tags"
render={arrayHelpers => (
<div>
{tagCollection.map(tag => (
<label key={tag.value}>
<input
name="tags"
type="checkbox"
value={tag}
checked={values.tags.includes(tag.value)}
onChange={e => {
if (e.target.checked) {
arrayHelpers.push(tag.value);
} else {
const idx = values.tags.indexOf(tag.value);
arrayHelpers.remove(idx);
}
}}
/>
<span>{tag.label}</span>
</label>
))}
</div>
)}
/>
Working sandbox
Update:
You can also do this by writing your own component.
const MyCheckbox = ({ field, form, label, ...rest }) => {
const { name, value: formikValue } = field;
const { setFieldValue } = form;
const handleChange = event => {
const values = formikValue || [];
const index = values.indexOf(rest.value);
if (index === -1) {
values.push(rest.value);
} else {
values.splice(index, 1);
}
setFieldValue(name, values);
};
return (
<label>
<input
type="checkbox"
onChange={handleChange}
checked={formikValue.indexOf(rest.value) !== -1}
{...rest}
/>
<span>{label}</span>
</label>
);
};
// And using it like this:
<Field component={MyCheckbox} name="tags" value="one" label="One" />
<Field component={MyCheckbox} name="tags" value="two" label="Two" />
<Field component={MyCheckbox} name="tags" value="three" label="Three" />
With Formik 2 without any Formik specific components:
import React from "react";
import { useFormik } from "formik";
export default function App() {
return <ProjectsForm></ProjectsForm>;
}
const tags = ["one", "two", "three"];
const ProjectsForm = () => {
const formik = useFormik({
enableReinitialize: true,
initialValues: {
tags: []
},
onSubmit: (values) => {
console.log(values);
}
});
const handleChange = (e) => {
const { checked, name } = e.target;
if (checked) {
formik.setFieldValue("tags", [...formik.values.tags, name]);
} else {
formik.setFieldValue(
"tags",
formik.values.tags.filter((v) => v !== name)
);
}
};
return (
<form onSubmit={formik.handleSubmit}>
{tags.map((tag) => (
<div key={tag}>
<input
id={tag}
type="checkbox"
name={tag}
checked={formik.values.tags.includes(tag)}
onChange={handleChange}
/>
<label htmlFor={tag}>{tag}</label>
</div>
))}
<button type="submit">Submit</button>
</form>
);
};
I started by creating a simple array of tags.
Then I used useFormik
hook to bind the form for an array of checkboxes. Whether a checkbox is checked, depends on whether it's in the tags array.
Hence: formik.values.tags.includes(tag)
I created a custom change handling function to set or remove a tag from Formik values depending on whether a checkbox is checked or not.
Full working Sandbox example
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With