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

时间:2020-04-28 07:52:54

标签: javascript forms checkbox material-ui react-hook-form

我正在尝试使用react-form-hook Material UI构建容纳多个“分组”复选框的表单。

该复选框是根据HTTP请求异步创建的。

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

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

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

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

我该如何实现?

这里是一个sample,我正在尝试重现该问题。

加分点,使用Yup验证最少选中的复选框

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

4 个答案:

答案 0 :(得分:7)

在6.X中进行的API更改:

  • 验证选项已更改为使用解析器函数包装器和其他配置属性名称
    注意:文档仅是为validateResolver-> resolver固定的,用于回购中验证的代码示例尚未更新(仍然使用validationSchema进行测试)。感觉好像他们不确定他们想对那里的代码做什么,并且它处于混乱状态。我会完全避免使用他们的Controller,直到它稳定下来,或者将Controller用作您自己的Controller HOC表单的精简包装,这似乎是他们想要进入的方向。
    请参见official sandbox demo"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不再本机处理Checkbox(type="checkbox"),或者更好的说,是错误地处理了值。它不会检测复选框的布尔值,而是尝试将其强制转换为字符串值。您有几种选择:
  1. 请勿使用Controller。使用不受控制的输入
  2. 使用新的render道具为您的Checkbox使用自定义渲染功能,并添加setValue挂钩
  3. 像表单控制器HOC一样使用Controller并手动控制所有输入

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


5.X的说明:

这是一个不需要Controller的简化示例。不受控制是文档中的建议。仍然建议您为每个输入提供自己的name并对数据进行转换/过滤以删除未经检查的值,例如在后一个示例中使用yup和validateatorSchema,但是出于示例目的,请使用相同的名称会导致将值添加到符合您要求的数组中。
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的模式,该模式将验证至少选择了2个模式,并在将数据发送到onSubmit之前将其转换为所需的模式。它会过滤掉错误的值,因此您将获得一个字符串ID数组:

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

答案 1 :(得分:5)

我也一直在为此苦苦挣扎,这是对我有用的。

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链接:https://codesandbox.io/s/material-demo-54nvi?file=/demo.js

另一种解决方案,其中默认选择的项目未使用useStatehttps://codesandbox.io/s/material-demo-bzj4i?file=/demo.js

答案 2 :(得分: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中的Checkbox可能应该返回选中的(布尔值)而不是(值)。

答案 3 :(得分:0)

这是我的解决方案,它不使用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({...})