Struggling to get MaterialUI 5 checkbox to work with Formik 2

Viewed 172

I'm having trouble getting Formik 5 and MUI 5 checkboxes to work together. In the Codesandbox below, you'll see a form with two unchecked checkboxes: "with MUI" and "without MUI".

The "without MUI" checkbox behaves as expected: I can check and uncheck it, and if I submit, I see withoutMui in the console with a value of true or false.

However, the "With MUI" checkbox exhibits different behavior. Rather than setting true or false, it creates an array of data. I get that this is done to handle multiple checkboxes grouped together. However, what I don't see is anywhere I can transform that array value into true or false before handing it off to the MUI checkbox. it looks like Typescript expects field.checked that comes back from useField to be a boolean rather than an array, so I'm not able to do any manipulation on that value without getting a Typescript error.

Interestingly, if I check the "with MUI" checkbox once, I see an array with 1 value (on). If I click the checkbox again, then I get an array with no values in it, but the checkbox UI does not update. So it looks like the checkbox data is getting properly updated, but the MUI checkbox can't deal with array data.

I feel like I'm doing something stupid here, and would appreciate being shown the error of my ways.

Thanks in advance!

https://codesandbox.io/s/wizardly-https-bb7up6

non-MUI Checkbox code from sandbox:

import { useField } from "formik";

const CheckboxWithoutMUI = ({ label, ...rest }) => {
  const [field, meta] = useField({ ...rest, type: "checkbox" });
  return (
    <div>
      <label>
        <input type="checkbox" {...field} {...rest} />
        {label}
      </label>
    </div>
  );
};

export default CheckboxWithoutMUI;

MUI Checkbox code from sandbox:

import { useField } from "formik";
import FormControlLabel from "@mui/material/FormControlLabel";
import Checkbox from "@mui/material/Checkbox";

interface IProps {
  label: string;
  name: string;
}

const CheckboxField = ({ label, ...rest }: IProps) => {
  const [field, meta] = useField({ ...rest, type: "checkbox" });

  return (
    <div>
      <FormControlLabel
        label={label}
        control={
          <Checkbox
            checked={field.checked}
            onChange={field.onChange}
            id={field.name}
          />
        }
      />
    </div>
  );
};

export default CheckboxField;
Software Version(s)
Formik 2.2.9
React 17.0.2
TypeScript 4.5.5
Browser Chrome (Mac) 101.0.4951.64
npm/Yarn 8.5.0
Operating System macOS 12.4
1 Answers

The difference in behavior between the two checkboxes is due to not giving the MUI checkbox a value. This causes Formik to manage the checkbox value differently.

If you remove the value prop from the non-MUI checkbox, it will behave the same as the MUI checkbox in your sandbox:

import { useField } from "formik";

const CheckboxWithoutMUI = ({ label, ...rest }) => {
  const [field, meta] = useField({ ...rest, type: "checkbox" });
  const { value, ...fieldExceptValue } = field;
  return (
    <div>
      <label>
        <input type="checkbox" {...fieldExceptValue} {...rest} />
        {label}
      </label>
    </div>
  );
};

export default CheckboxWithoutMUI;

Edit blissful-thompson-soj2uk

If you change the syntax of the MUI Checkbox to be more similar to your non-MUI version (pass all of the field props to it), it works as desired:

import { useField } from "formik";
import FormControlLabel from "@mui/material/FormControlLabel";
import Checkbox from "@mui/material/Checkbox";

interface IProps {
  label: string;
  name: string;
}

const CheckboxField = ({ label, ...rest }: IProps) => {
  const [field] = useField({ ...rest, type: "checkbox" });
  return (
    <div>
      <FormControlLabel label={label} control={<Checkbox {...field} />} />
    </div>
  );
};

export default CheckboxField;

Edit frosty-newton-n9h5pd

It is also sufficient to just add value to what you already had:

import { useField } from "formik";
import FormControlLabel from "@mui/material/FormControlLabel";
import Checkbox from "@mui/material/Checkbox";

interface IProps {
  label: string;
  name: string;
}

const CheckboxField = ({ label, ...rest }: IProps) => {
  const [field] = useField({ ...rest, type: "checkbox" });

  return (
    <div>
      <FormControlLabel
        label={label}
        control={
          <Checkbox
            checked={field.checked}
            onChange={field.onChange}
            id={field.name}
            value={field.value}
          />
        }
      />
    </div>
  );
};

export default CheckboxField;

Edit weathered-silence-e5lo60

Related