form icon indicating copy to clipboard operation
form copied to clipboard

Usage with Checkboxes

Open timcosta opened this issue 5 years ago • 5 comments

Hey all -

I'm trying to use v4 with Material-UI Checkboxes, and am experiencing some strange behavior.

const Checkbox = ({
  field,
  label,
  checked,
}) => {

    const {
        getInputProps,
        value,
    } = useField(field, {
        defaultValue: checked,
    });

    return (
        <FormControlLabel control={
            <MuiCheckbox
                {...getInputProps()}
                checked={value}
            />
        } label={label} />
    );
};

Here's a stripped down version of my component. This works when I pass a default value of true or false but the output of the value is a stringified "true" or "false" after I manipulate the checkbox in the UI. Is there a way to tell react-form that this field is supposed to be a checkbox boolean, or do i need to add handling for stringified booleans to my code base?

timcosta avatar Dec 22 '19 21:12 timcosta

const Checkbox = ({
  field,
  label,
  checked,
}) => {

    const {
        getInputProps,
        setValue,
    } = useField(field, {
        defaultValue: checked,
    });

    const onChange = (e) => {
      setValue(e.target.checked);
    }

    return (
        <FormControlLabel control={
            <MuiCheckbox
                {...getInputProps()}
                onChange={onChange}
            />
        } label={label} />
    );
};

This works, with a custom onChange that calls setValue. It just feels like there should be an easier way. I tried filterValue but returning false caused the stored value to not update.

timcosta avatar Dec 22 '19 22:12 timcosta

I went down the same path as @timcosta. Seems like checkboxes don’t play by the same rules as other inputs, since they use defaultChecked and checked for their value, and this might be confusing this library if it expects the value to be coming from value.

A simple example of how to use react-form with <input type="checkbox"> would be helpful.

jacksonrayhamilton avatar Dec 31 '19 23:12 jacksonrayhamilton

@jacksonrayhamilton here's an updated version of what i'm using, with support for creating an array based on a set of checkbox values.

import React from 'react';
import PropTypes from 'prop-types';
import { useField } from 'react-form';
import { Checkbox as MuiCheckbox, FormControlLabel } from '@material-ui/core';

const findValueInArray = (array = [], value) => {
  if (typeof value === 'object' && value.constructor === Object && value.id) {
    return array.findIndex((i) => i.id === value.id);
  }
  return array.indexOf(value);
}

const Checkbox = ({
  field,
  label,
  isArray,
  value,
}) => {

    if (isArray && !value) throw new Error('Checkboxes that are used as for array must have a value.');
    const methods = useField(field);
    let currentValue = methods.value;
    if (currentValue === undefined) {
      if (isArray) {
        currentValue = [];
      } else {
        currentValue = false;
      }
      methods.setValue(currentValue);
    }

    const onChange = (e) => {
      if (e.target.checked) {
        if (isArray) {
          methods.pushValue(value);
        } else {
          methods.setValue(value || e.target.checked);
        }
      } else {
        if (isArray) {
          const idx = findValueInArray(methods.value, value);
          if (idx > -1) methods.removeValue(idx);
        } else {
          methods.setValue(value ? undefined : false);
        }
      }
    }

    return (
        <FormControlLabel control={
            <MuiCheckbox
                {...methods.getInputProps()}
                checked={isArray ? findValueInArray(methods.value, value) > -1 : currentValue}
                onChange={onChange}
                value={value}
            />
        } label={label} />
    );
};

Checkbox.propTypes = {
  label: PropTypes.string.isRequired,
  field: PropTypes.string.isRequired,
  isArray: PropTypes.bool,
  value: PropTypes.any,
};

Checkbox.defaultProps = {};

export default Checkbox;

It's not the cleanest, but it supports create and edit (via defaultValues on useForm) for both single and group/array checkboxes. Can probably be optimized from a perf perspective with some useMemo or useEffect hooks.

timcosta avatar Dec 31 '19 23:12 timcosta

Thanks Tim for this! When I said “A simple example … would be helpful,” I actually meant to direct that to the package maintainer. Since you said “It just feels like there should be an easier way,” and since I agree with that, I was hoping we could get a canonical answer; one which would hopefully feel consistent with the style used for other inputs.

jacksonrayhamilton avatar Dec 31 '19 23:12 jacksonrayhamilton

Definitely agreed, it would be great to have official checkbox support. Just wanted to share how I solved it as to unblock you just in case.

timcosta avatar Dec 31 '19 23:12 timcosta