材质UI+React Form Hook+多个复选框+默认选中



我正在尝试使用react-form-hookMaterial UI构建一个包含多个"分组"复选框的表单。

复选框是通过HTTP请求异步创建的。

我想提供一个对象ID数组作为默认值:

defaultValues: { boat_ids: trip?.boats.map(boat => boat.id.toString()) || [] }

此外,当我选择或取消选择复选框时,我希望将对象的ID添加/删除到react-hook-form的值中。

即。(boat_ids: [25, 29, 4](

我怎样才能做到这一点?

这是我试图重现这个问题的一个样本。

奖励积分,使用Yup 验证最小选中复选框

boat_ids: Yup.array() .min(2, "")

我也一直在为此而挣扎,以下是对我有效的方法。

更新了react hook表单v6的解决方案,也可以在没有useState的情况下完成(下面的沙箱链接(:

import React, { useState } from "react";
import { useForm, Controller } from "react-hook-form";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Checkbox from "@material-ui/core/Checkbox";
export default function CheckboxesGroup() {
const defaultNames = ["bill", "Manos"];
const { control, handleSubmit } = useForm({
defaultValues: { names: defaultNames }
});
const [checkedValues, setCheckedValues] = useState(defaultNames);
function handleSelect(checkedName) {
const newNames = checkedValues?.includes(checkedName)
? checkedValues?.filter(name => name !== checkedName)
: [...(checkedValues ?? []), checkedName];
setCheckedValues(newNames);
return newNames;
}
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
{["bill", "luo", "Manos", "user120242"].map(name => (
<FormControlLabel
control={
<Controller
name="names"
render={({ onChange: onCheckChange }) => {
return (
<Checkbox
checked={checkedValues.includes(name)}
onChange={() => onCheckChange(handleSelect(name))}
/>
);
}}
control={control}
/>
}
key={name}
label={name}
/>
))}
<button>Submit</button>
</form>
);
}

Codesandbox链接:https://codesandbox.io/s/material-demo-54nvi?file=/demo.js

另一个具有默认选定项目的解决方案在没有useState的情况下完成:https://codesandbox.io/s/material-demo-bzj4i?file=/demo.js

打破6.X:中所做的API更改

  • 验证选项已更改为使用解析程序函数包装器和不同的配置属性名称
    注意:文档刚刚修复用于验证resolver->解析器和repo中验证的代码示例尚未更新(仍使用validationSchema进行测试(。感觉好像他们不确定他们想对那里的代码做什么,而且它处于一种不确定的状态。我会完全避开他们的Controller,直到它稳定下来,或者使用Controller作为您自己的表单Controller HOC的瘦包装,这似乎是他们想要的方向。
    请参阅官方沙盒演示和"false"值作为复选框字符串的意外行为,以供参考
import { yupResolver } from "@hookform/resolvers";
const { register, handleSubmit, control, getValues, setValue } = useForm({
resolver: yupResolver(schema),
defaultValues: Object.fromEntries(
boats.map((boat, i) => [
`boat_ids[${i}]`,
preselectedBoats.some(p => p.id === boats[i].id)
])
)
});
  • Controller不再以本机方式处理复选框(type="checkbox"(,或者更确切地说,不正确地处理值。它不检测复选框的布尔值,并尝试将其强制转换为字符串值。您有几个选择:
  1. 不要使用Controller。使用不受控制的输入
  2. 使用新的render道具为复选框使用自定义渲染函数,并添加setValue挂钩
  3. 像表单控制器HOC一样使用控制器,并手动控制所有输入

避免使用控制器的示例:
https://codesandbox.io/s/optimistic-paper-h39lq
https://codesandbox.io/s/silent-mountain-wdiov
与第一个原始示例相同,但使用yupResolver包装


5.X的描述:

下面是一个不需要Controller的简化示例。不受控制是文件中的建议。仍然建议您为每个输入提供自己的name,并对数据进行转换/筛选以删除未检查的值,例如后一个示例中的yup和validatorSchema,但就示例而言,使用相同的名称会将值添加到符合您需求的数组中
https://codesandbox.io/s/practical-dijkstra-f1yox

无论如何,问题是defaultValues与复选框的结构不匹配。它应该是{[name]: boolean},其中生成的names文本字符串boat_ids[${boat.id}],直到它通过将值聚集到一个数组中的不受控制的表单输入。例如:form_input1[0] form_input1[1]发射form_input1 == [value1, value2]

https://codesandbox.io/s/determined-paper-qb0lf

构建defaultValues: { "boat_ids[0]": false, "boat_ids[1]": true ... }
控制器需要布尔值来切换复选框值,并将其作为默认值提供给复选框。

const { register, handleSubmit, control, getValues, setValue } = useForm({
validationSchema: schema,
defaultValues: Object.fromEntries(
preselectedBoats.map(boat => [`boat_ids[${boat.id}]`, true])
)
});

用于validationSchema的Schema,用于验证至少选择了2个,并在将数据发送到onSubmit之前将数据转换为所需的Schema。它过滤掉了错误的值,所以你得到了一个字符串id数组:

const schema = Yup.object().shape({
boat_ids: Yup.array()
.transform(function(o, obj) {
return Object.keys(obj).filter(k => obj[k]);
})
.min(2, "")
});

这里有一个工作版本:

import React from "react";
import { useForm, Controller } from "react-hook-form";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Checkbox from "@material-ui/core/Checkbox";
export default function CheckboxesGroup() {
const { control, handleSubmit } = useForm({
defaultValues: {
bill: "bill",
luo: ""
}
});
return (
<form onSubmit={handleSubmit(e => console.log(e))}>
{["bill", "luo"].map(name => (
<Controller
key={name}
name={name}
as={
<FormControlLabel
control={<Checkbox value={name} />}
label={name}
/>
}
valueName="checked"
type="checkbox"
onChange={([e]) => {
return e.target.checked ? e.target.value : "";
}}
control={control}
/>
))}
<button>Submit</button>
</form>
);
}

codesandbox链接:https://codesandbox.io/s/material-demo-65rjy?file=/demo.js:0-932

但是,我不建议这样做,因为材质UI中的复选框可能应该返回checked(boolean(而不是(value(。

这是我的解决方案,它没有使用Material UI中的所有默认组件,因为在我的界面上,每个收音机都会有一个图标和文本,除了不会显示默认的项目符号:

const COMPANY = "company";
const INDIVIDUAL = "individual";
const [scope, setScope] = useState(context.scope || COMPANY);
const handleChange = (event) => {
event.preventDefault();
setScope(event.target.value);
};
<Controller
as={
<FormControl component="fieldset">
<RadioGroup
aria-label="scope"
name="scope"
value={scope}
onChange={handleChange}
>
<FormLabel>
{/* Icon from MUI */}
<Business />
<Radio value={COMPANY} />
<Typography variant="body1">Company</Typography>
</FormLabel>
<FormLabel>
{/* Icon from MUI */}
<Personal />
<Radio value={INDIVIDUAL} />
<Typography variant="body1">Individual</Typography>
</FormLabel>
</RadioGroup>
</FormControl>
}
name="scope"
control={methods.control}
/>;

观察:在这个例子中,我使用不带析构函数的React Hook Form:

const methods = useForm({...})

这是我使用react hook表单7的解决方案,其他解决方案不适用于reset或setValue。

<Controller
name={"test"}
control={control}
render={({ field }) => (
<FormControl>
<FormLabel id={"test"}>{"label"}</FormLabel>
<FormGroup>
{items.map((item, index) => {
const value = Object.values(item);
return (
<FormControlLabel
key={index}
control={
<Checkbox
checked={field.value.includes(value[0])}
onChange={() =>
field.onChange(handleSelect(value[0],field.value))
}
size="small"
/>
}
label={value[1]}
/>
);
})}
</FormGroup>
</FormControl>
)}
/>

链接到codesandbox:Mui多复选框

最新更新