Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Formik checkbox group does not turn into an array of invidual checked or unchecked elements

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.

like image 413
niklasbuhl Avatar asked Aug 26 '19 08:08

niklasbuhl


People also ask

How do I validate a checkbox in Formik?

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 .

How do I add a checkbox in Formik?

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.

How do I reset a checkbox in Formik?

You can use Formik's setFieldValue function to reset the field when the checkbox is unchecked.

How use material UI checkbox with Formik?

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.


2 Answers

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" />
like image 162
Jap Mul Avatar answered Oct 04 '22 04:10

Jap Mul


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

like image 25
Branislav Lazic Avatar answered Oct 04 '22 03:10

Branislav Lazic