form
form copied to clipboard
Usage with Checkboxes
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?
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.
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 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.
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.
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.