[英]Material UI + React Form Hook + multiple checkboxes + default selected
I am trying to build a form that accommodates multiple 'grouped' checkboxes using react-form-hook
Material UI
.我正在尝试使用
react-form-hook
Material UI
构建一个可容纳多个“分组”复选框的表单。
The checkboxes are created async from an HTTP Request.复选框是从 HTTP 请求异步创建的。
I want to provide an array of the objects IDs as the default values:我想提供一组对象 ID 作为默认值:
defaultValues: { boat_ids: trip?.boats.map(boat => boat.id.toString()) || [] }
Also, when I select or deselect a checkbox, I want to add/remove the ID of the object to the values of react-hook-form
.此外,当我 select 或取消选中一个复选框时,我想将object 的 ID添加/删除到
react-hook-form
的值。
ie. IE。 (
boat_ids: [25, 29, 4]
) (
boat_ids: [25, 29, 4]
)
How can I achieve that?我怎样才能做到这一点?
Here is a sample that I am trying to reproduce the issue.这是我试图重现该问题的示例。
Bonus point, validation of minimum selected checkboxes using Yup加分,使用 Yup 验证最小选中复选框
boat_ids: Yup.array().min(2, "")
Breaking API changes made in 6.X:破坏 6.X 中所做的 API 更改:
validationSchema
for tests).validationSchema
进行测试)。 It feels as if they aren't sure what they want to do with the code there, and it is in a state of limbo."false"
value as a string of the Checkbox for reference"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
no longer handles Checkbox natively ( type="checkbox"
), or to better put it, handles values incorrectly. Controller
不再原生处理 Checkbox ( type="checkbox"
),或者更好地说,错误地处理值。 It does not detect boolean values for checkboxes, and tries to cast it to a string value.Controller
.Controller
。 Use uncontrolled inputsrender
prop to use a custom render function for your Checkbox and add a setValue hookrender
道具为您的复选框使用自定义渲染 function 并添加 setValue 挂钩Examples avoiding the use of Controller:避免使用 Controller 的示例:
https://codesandbox.io/s/optimistic-paper-h39lq https://codesandbox.io/s/optimistic-paper-h39lq
https://codesandbox.io/s/silent-mountain-wdiov https://codesandbox.io/s/silent-mountain-wdiov
Same as first original example but using yupResolver
wrapper与第一个原始示例相同,但使用
yupResolver
包装器
Description for 5.X: 5.X 说明:
Here is a simplified example that doesn't require Controller.这是一个不需要 Controller 的简化示例。 Uncontrolled is the recommendation in the docs.
不受控制的是文档中的建议。 It is still recommended that you give each input its own
name
and transform/filter on the data to remove unchecked values, such as with yup and validatorSchema in the latter example, but for the purpose of your example, using the same name causes the values to be added to an array that fits your requirements.仍然建议您为每个输入提供自己的
name
并在数据上进行转换/过滤以删除未检查的值,例如后一个示例中的 yup 和 validatorSchema,但对于您的示例而言,使用相同的名称会导致值添加到符合您要求的数组中。
https://codesandbox.io/s/practical-dijkstra-f1yox https://codesandbox.io/s/practical-dijkstra-f1yox
Anyways, the problem is that your defaultValues
doesn't match the structure of your checkboxes.无论如何,问题在于您的
defaultValues
与复选框的结构不匹配。 It should be {[name]: boolean}
, where names
as generated is the literal string boat_ids[${boat.id}]
, until it passes through the uncontrolled form inputs which bunch up the values into one array.它应该是
{[name]: boolean}
,其中生成的names
是文字字符串boat_ids[${boat.id}]
,直到它通过不受控制的表单输入,这些输入将值捆绑到一个数组中。 eg: form_input1[0] form_input1[1]
emits form_input1 == [value1, value2]
例如:
form_input1[0] form_input1[1]
发出form_input1 == [value1, value2]
https://codesandbox.io/s/determined-paper-qb0lf https://codesandbox.io/s/determined-paper-qb0lf
Builds defaultValues: { "boat_ids[0]": false, "boat_ids[1]": true... }
构建
defaultValues: { "boat_ids[0]": false, "boat_ids[1]": true... }
Controller expects boolean values for toggling checkbox values and as the default values it will feed to the checkboxes. Controller 需要 boolean 值来切换复选框值,并将其作为默认值提供给复选框。
const { register, handleSubmit, control, getValues, setValue } = useForm({
validationSchema: schema,
defaultValues: Object.fromEntries(
preselectedBoats.map(boat => [`boat_ids[${boat.id}]`, true])
)
});
Schema used for the validationSchema, that verifies there are at least 2 chosen as well as transforms the data to the desired schema before sending it to onSubmit.用于 validationSchema 的模式,用于验证至少选择了 2 个,并将数据转换为所需的模式,然后再将其发送到 onSubmit。 It filters out false values, so you get an array of string ids:
它会过滤掉错误值,因此您会得到一个字符串 id 数组:
const schema = Yup.object().shape({
boat_ids: Yup.array()
.transform(function(o, obj) {
return Object.keys(obj).filter(k => obj[k]);
})
.min(2, "")
});
I've been struggling with this as well, here is what worked for me.我也一直在为此苦苦挣扎,这对我有用。
Updated solution for react-hook-form v6, it can also be done without useState
(sandbox link below): react-hook-form 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 link: https://codesandbox.io/s/material-demo-54nvi?file=/demo.js Codesandbox 链接: https://codesandbox.io/s/material-demo-54nvi?file=/demo.js
Another solution with default selected items done without useState
: https://codesandbox.io/s/material-demo-bzj4i?file=/demo.js另一个没有
useState
完成默认选定项目的解决方案: https://codesandbox.io/s/material-demo-bzj4i?file=/demo.js
Here is a working version:这是一个工作版本:
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 link: https://codesandbox.io/s/material-demo-65rjy?file=/demo.js:0-932代码沙盒链接: https://codesandbox.io/s/material-demo-65rjy?file=/demo.js:0-932
However, I do not recommend doing so, because Checkbox in material UI probably should return checked (boolean) instead of (value).但是,我不建议这样做,因为 Material UI 中的 Checkbox 可能应该返回选中的(布尔值)而不是(值)。
Here's my solution, which is not using all the default components from Material UI cause at my interface each radio will have an icon and text, besides the default bullet point not be showed:这是我的解决方案,它没有使用 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}
/>;
Observation : At this example I use React Hook Form without destruct:观察:在这个例子中,我使用没有破坏的 React Hook Form:
const methods = useForm({...})
This is my solution with react hook form 7, the other solutions don't work with reset or setValue.这是我使用 react hook form 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>
)}
/>
link to codesandbox: Mui multiple checkbox codesandbox 链接: Mui 多复选框
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.