在React中设置复选框的“check”属性

65

我在React和复选框方面遇到了一个非常烦人的问题。我正在处理的应用程序需要一个复选框列表,表示在后端持久化的设置。有一个选项可以将设置恢复为其原始状态

起初,我创建了一个具有类似于设置映射的对象的组件。每个设置都有一个键和布尔值。因此:

{
    bubbles: true,
    gregory: false
}

应该被表示为:

<input type="checkbox" value="bubbles" checked="checked" />
<input type="checkbox" value="gregory" />

现在,React似乎不清楚复选框的工作原理。我不想设置复选框的值,我想要的是"checked"属性。

然而,如果我尝试像这样:

<input
    type="checkbox"
    value={setting}
    checked={this.settings[setting]}
    onChange={this.onChangeAction.bind(this)}
/>

我收到了这个警告:

警告:AwesomeComponent 将一个未控制的复选框输入改为受控。输入元素不应从未受控制转换为受控制(或反之亦然)。在组件的生命周期内决定使用控制或未控制的输入元素。更多信息:[我阅读了几遍但毫无进展的一些无用文档页面]

因此,我决定创建另一个组件来包装每个单独的复选框,然后我得到了这个:

<input
    type="checkbox"
    checked={this.state.checked}
    onChange={this.onChangeAction.bind(this)}
/>

现在,checked是我状态中直接存在的属性。

这会产生相同的警告,因此我尝试使用defaultChecked

<input
    type="checkbox"
    defaultChecked={this.state.checked}
    onChange={this.onChangeAction.bind(this)}
/>

这使得警告消失了,但现在无法将checked值重置为默认值。所以我尝试使用componentWillReceiveProps方法进行调整,这样我非常确定我的状态是正确的,this.state.checked是正确的,组件重新渲染。

它确实做到了。 但复选框仍然保持原样。 目前我留下了那个丑陋的警告,正在使用checked。 如何修复这个问题,让警告消失?

我认为也许有一种方法可以强制重新渲染组件,这样它就可以捕获新的defaultChecked值并使用它。 但我不知道该怎么做。 也许仅针对此组件抑制警告? 这可能吗? 也许还有其他可行的东西?

6个回答

109

如果你将复选框的checked属性设置为nullundefined,就会出现问题。

在JS中,这些是“falsy”值。然而,React 将一个值为 null 的属性视为未设置该属性。由于复选框的默认状态为未选中,因此一切都运行正常。但是,如果你将checked设置为true,React认为该属性突然存在了!这时React意识到你从不受控制的变成了受控制的形式,因为现在有了checked属性。

在你的示例中,你可以通过将checked={this.settings[setting]}更改为checked={!!this.settings[setting]}来消除这个警告。注意双重否定符号(!!)。它们会将nullundefined转换为false(保持true不变),因此React将使用false值注册你的checked属性,并以受控表单组件的形式启动。

我也遇到过这个问题,多次阅读关于受控组件的文档,却无济于事。但最终我解决了这个问题,所以我想分享一下。此外,自15.2.0版本以来,通过设置value将普通输入字段设为受控制状态,而通过设置checked将复选框初始化为受控制状态,无论其value属性如何设置,这增加了一些混淆。


3
因为看到 React 给出的一个非常具体的警告信息,我误以为自己理解了问题,结果浪费了一个多小时的时间。事实上,是由于一个未定义的变量后来才被定义了。谢谢。 - Don
2
我无法为这个答案点赞足够多。在普通的HTML时代,有条件地添加和删除“checked”属性本身是一种好的实践。我花了二十分钟困惑于为什么会出现这个错误信息。ReactJS的错误信息真的应该有一个特殊情况来认识到不一致的怪异之处,即HTML复选框输入,以便其他人也不会碰到这个问题。 - Sean Werkema
1
非常棒的答案 :) 这是一个我不知道的好技巧(使用双感叹号来立即转换为布尔值) - Eric Martin
我之前也没有意识到复选框是由“checked”属性而不是“value”控制的。在找到这篇文章之前,我一直遇到一些奇怪的行为。谢谢!https://reactjs.org/docs/forms.html - HaulinOats

16

Amoebe的答案是正确的,但我认为比双重否定符(!!)更清晰的解决方案是在你的复选框组件的checked属性中添加一个值为falsedefaultProps属性:

import React from 'react';

const Checkbox = ({checked}) => (
  <div>
      <input type="checkbox" checked={checked} />
  </div>
);

Checkbox.defaultProps = {
  checked: false
};

export default Checkbox;

2
在函数声明中只写{checked = false}怎么样? - Théophile
是的,这对我来说似乎有些过度了。 - JSON

10

基本上,defaultChecked 的意思是您不想控制输入 - 它只会以这个值呈现,然后就没有办法控制它了。同时,不应该使用 value,而应该使用 checked,所以您的第二段代码应该是正确的。并且不应同时使用它们两个。

<input
    type="checkbox"
    checked={this.state.checked}
    onChange={this.onChangeAction.bind(this)}
/>

你能否创建一个小的示例来展示这种行为?


他的问题是因为this.state.checked未定义,而不是false。所以像这样做会更安全一些:<input type="checkbox" checked={!!this.state.checked} onChange={this.onChangeAction.bind(this)} /> - JohnFlux

5

如果你选择将类组件转换为函数组件,下面是使用钩子的答案...

export default Checklist = () => {
  const [listOfItems, setListOfItems] = useState([
    {name: 'bubbles', isChecked: true},
    {name: 'gregory', isChecked: false}
  ]);

  const updateListOfItems = (itemIndex, isChecked) => {
    const updatedListOfItems = [...listOfItems];
    updatedListOfItems[itemIndex].isChecked = isChecked;
    setListOfItems(updatedListOfItems);
  }

  return (
    {listOfitems.map((item, index) =>
      <index key={index} type="checkbox" checked={item.isChecked} onChange={() => updateListOfItems(index, !item.isChecked)} />
    )}
  )
}

这对我有帮助,但有拼写错误。 - Jeremy
1
@Jeremy 谢谢,我更新了答案以修复拼写错误。 - Fiddle Freak

0
您可以将数据分配给状态,然后利用与各个复选框关联的checked属性来设置状态。
{   this.state.data.map(function(item, index) {
      return (
        
        <input type="checkbox" checked={item.value} onChange={this.handleChange.bind(this, index)}/>
        
      );
    }.bind(this))
    }

状态中的示例数据如下

data: [{name:'bubbles', value: true}, {name:'gregory', value: false}]

JSFIDDLE

的内容与编程有关。

0

结合其他答案,最终解决方案如下:

const [categories, setCategories] = useState(
    [
        {
            field: 'Label 1',
            checked: true
        },
        {
            field: 'Label 2',
            checked: false
        },
        {
            field: 'Label 3',
            checked: false
        }
    ]
)
const updateList = (item, isChecked) => {
    const updatedList = [...categories];
    updatedList[item].checked = isChecked;
    setCategories(updatedList);
}
... in your markup:
{
  categories.map((item, index) =>
    <div key = {item.field}>
      <label>
        <span>{item.field}</span>
        <input key={index}
               type="checkbox"
               checked={item.checked}
               onChange={() => updateList(index, !item.checked)}
         />
      </label>
    </div>
  )
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接