【问题标题】:How do I Map several controlled input checkboxes from an array in React?如何从 React 中的数组映射多个受控输入复选框?
【发布时间】:2021-06-12 02:37:28
【问题描述】:

我正在尝试从这样的数组中创建一些受控复选框

const subjects = [
    {
      name: 'WebDev',
      label: 'Web Development',
    },
    {
      name: 'GraphicDesign',
      label: 'Graphic Design',
    },
    {
      name: 'Engineering',
      label: 'Engineering',
    },
  ];

我想让它们受控,所以对于checked属性,我需要它被状态控制。但是我如何遍历它们并每次访问正确的状态对象?我想我也许可以解析 subject.name 字符串,但这肯定不是好习惯吗?事实上,我也许可以在索引匹配的状态下创建一个匹配数组,但我不觉得这是可扩展的并且严重依赖于正确的顺序。

{subjects.map((subject, i) => (
  <div className="w-full sm:w-auto" key={i}>
  <label className="flex content-center p-3">
    <input type="checkbox" name={subject.name} value={subject.name} onChange={onInputChange} checked={????}/>
    <span className="ml-3">{subject.label}</span>
  </label>
</div>
))}

我还有其他类似的输入。对他们来说,这很容易,因为我可以使用state.name 来控制他们。

      <input id="name" type="text" value={state.name} onChange={onInputChange} name="name" required/>

然后我使用广义函数来更新输入。这需要使用useState() Hook

    const onInputChange = e => {

    const item = e.target.name;
    const value = e.target.type === "checkbox" ? e.target.checked : e.target.value;

    setState({
      ...state,
      [item]: value,
    })
  }

在 React 中处理此类问题的最佳实践是什么?如果我可以在我的状态下包含如下所示的主题,那就太好了。

我是否应该放弃单独的数组并包含状态中的所有信息?

const [state, setState] = useState({
    name:'',
    email: '',
    message: '',
    checkedSubs: {
      WebDev: false,
      GraphicDesign: false,
      Engineering: false
    },
    emailSent: false
})

谢谢!

【问题讨论】:

  • 您是否考虑过为您的主题对象添加另一个属性? checked: false 之类的东西?
  • 这个subjects数组是从哪里来的?它是组件的道具吗?有变化吗?
  • 如果你想使用相同的onInputChange 来处理复选框,那么它们需要是状态的顶级属性,而不是嵌套的。 checked={state[subject.name]}.

标签: javascript reactjs forms checkbox


【解决方案1】:

通用onInputChange

您当前的onInputChange 处理程序根据input 的name 属性更新state 中的属性。实现这一点的最简单方法之一是让此处理程序像更新任何其他字段一样更新复选框。

这意味着您的主题将成为state 的顶级属性。只要主题名称不可能与"email" 之类的表单字段相同,就可以了。您可以根据需要将state 重新排列到其他变量中。

您可以映射到您的示例格式:

// can't use variable subjects because it's already defined
const {name, email, message, emailSent, ...subjectBooleans} = state;

const formatted = {name, email, message, emailSent, subjects: subjectBooleans}

你只能得到一个包含被检查主题名称的数组:

const checkedSubjects = subjects
  .filter(o => state[o.name])
  .map( o => o.name );

我不确定subjects 数组的来源以及它是常量还是可能会改变的东西。您可以从初始状态中省略这些属性,并假设 undefined 表示 false/unchecked。我们通过查看state[subject.name] 来查看是否检查了一个主题,我们可以使用!! 显式转换为boolean 以更好地处理这种undefined 场景。

const MyForm = ({ subjects }) => {
  
  // removed initial values for subjects
  // you could set these, but they aren't required
  const [state, setState] = useState({
    name: "",
    email: "",
    message: "",
    emailSent: false
  });

  // no changes to this function
  const onInputChange = (e) => {
    const item = e.target.name;
    const value =
      e.target.type === "checkbox" ? e.target.checked : e.target.value;

    setState({
      ...state,
      [item]: value
    });
  };

  return (
    <form>
      <input
        id="name"
        type="text"
        value={state.name}
        onChange={onInputChange}
        name="name"
        required
      />
      {/* ... other inputs ... */}
      {subjects.map((subject, i) => (
        <div className="w-full sm:w-auto" key={i}>
          <label className="flex content-center p-3">
            <input
              type="checkbox"
              name={subject.name}
              value={subject.name}
              onChange={onInputChange}
              checked={!!state[subject.name]}
            />
            <span className="ml-3">{subject.label}</span>
          </label>
        </div>
      ))}
    </form>
  );
};

【讨论】:

    【解决方案2】:

    使用多个状态来跟踪主题的状态和您的一般数据。给每个主题一个名为check 的额外属性,它应该包含一个布尔值。该值将确定复选框的选中状态。

    在onInputChange 函数中,使用输入的新检查状态更新subjects 状态。

    添加一个具有依赖关系的useEffect 钩子:subjects 数组。每当subjects 的值发生变化时,都会触发useEffect 回调函数内部的逻辑。在该函数中,根据 subjects 数组中的值更新您的一般状态。

    最终结果将是subjects 数组将确定您的输入。每当更改输入时,subjects 状态将被更新。然后当subjects 发生变化时,输入将被重新渲染,data 中的值将根据变化进行更新。

    const ExampleComponent = () => {
      const [ subjects, setSubjects ] = useState([
        {
          name: 'WebDev',
          label: 'Web Development',
          checked: false
        },
        {
          name: 'GraphicDesign',
          label: 'Graphic Design',
          checked: false
        },
        {
          name: 'Engineering',
          label: 'Engineering',
          checked: false
        },
      ]);
    
      const [ data, setData ] = useState({
        name:'',
        email: '',
        message: '',
        checkedSubs: {
          WebDev: false,
          GraphicDesign: false,
          Engineering: false
        },
        emailSent: false
      });
    
      const onInputChange = e => {
        const item = e.target.name;
        const isChecked = e.target.checked;
    
        const updatedSubjects = subjects.map(subject => {
          if (subject.name === item) {
            return {
              ...subject,
              checked: isChecked
            }
          }
    
          return subject;
        });
    
        setSubjects(updatedSubjects);
      };
    
      useEffect(() => {
        setData({
          ...data,
          checkedSubs: subjects.reduce((acc, cur) => {
            acc[cur.name] = cur.checked;
            return acc;
          }, {})
        });
      }, [subjects]);
    
      return (
        subjects.map(({ name, label, checked }, i) => (
          <div className="w-full sm:w-auto" key={i}>
            <label className="flex content-center p-3">
              <input type="checkbox" name={name} value={name} onChange={onInputChange} checked={checked}/>
              <span className="ml-3">{label}</span>
            </label>
          </div>
        ))
      )
    }
    

    【讨论】:

    • 如果选中状态处于其自身状态subjects,那么它不应该也处于data 状态。我看到他们要求这样做,但这没有意义。只需创建一个结合subjects 和data 的formData 变量,而不必使用useEffect 并进行第二次不必要的状态更新。
    • @LindaPaiste 我同意。在没有任何变通方法的情况下,将它们全部组合到一个对象中会容易得多。不过,为了演示useEffect的用法,我相信这是一个可以接受的使用场景。
    猜你喜欢
    • 2015-04-20
    • 2021-02-14
    • 1970-01-01
    • 1970-01-01
    • 2020-08-21
    • 2021-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多