React复选框未发送onChange事件

203

TLDR: 使用 defaultChecked 替代 checked,在这个 jsbin 中可行。

尝试设置一个简单的复选框,当它被选中时划掉其标签文本。但是无论什么原因,在使用组件时 handleChange 都没有被触发。有人能解释一下我做错了什么吗?

var CrossoutCheckbox = React.createClass({
  getInitialState: function () {
    return {
        complete: (!!this.props.complete) || false
      };
  },
  handleChange: function(){
    console.log('handleChange', this.refs.complete.checked); // Never gets logged
    this.setState({
      complete: this.refs.complete.checked
    });
  },
  render: function(){
    var labelStyle={
      'text-decoration': this.state.complete?'line-through':''
    };
    return (
      <span>
        <label style={labelStyle}>
          <input
            type="checkbox"
            checked={this.state.complete}
            ref="complete"
            onChange={this.handleChange}
          />
          {this.props.text}
        </label>
      </span>
    );
  }
});

使用方法:

React.renderComponent(CrossoutCheckbox({text: "Text Text", complete: false}), mountNode);

解决方法:

使用 checked 属性不会改变底层值(显然),因此也不会调用 onChange 处理程序。将其更改为 defaultChecked 似乎可以解决这个问题:

var CrossoutCheckbox = React.createClass({
  getInitialState: function () {
    return {
        complete: (!!this.props.complete) || false
      };
  },
  handleChange: function(){
    this.setState({
      complete: !this.state.complete
    });
  },
  render: function(){
    var labelStyle={
      'text-decoration': this.state.complete?'line-through':''
    };
    return (
      <span>
        <label style={labelStyle}>
          <input
            type="checkbox"
            defaultChecked={this.state.complete}
            ref="complete"
            onChange={this.handleChange}
          />
          {this.props.text}
        </label>
      </span>
    );
  }
});

4
首先,为什么不添加一个onChange事件,只需执行this.setState({checked: !this.state.checked})即可,这比存储一个值更容易。然后在checked属性中使用一个三元运算符:checked={this.state.checked ? 'checked': null} - zackify
那就是它的起点,但它似乎从未更新。所以我开始在这里和那里添加一些内容来调试没有被触发的部分。最理想的情况是在完成后回到最简单的形式 :) - jdarling
2
假设您的mountNode是一个实际的dom节点,您将不得不使用this.refs.complete.getDOMNode().checked。请参见fiddle http://jsfiddle.net/d10xyqu1/。 - trekforever
他可以直接使用状态而不是获取DOM节点:http://jsfiddle.net/d10xyqu1/1/。它可以正常工作,你一定打错了什么。 - zackify
4
忽略TLDR评论——defaultChecked并不总是答案。 - Chris
9个回答

300

要获取复选框的选中状态,路径应该是:

this.refs.complete.state.checked

另一种方法是从传递到handleChange方法中的事件中获取它:

event.target.checked

5
无论你点击复选框还是标签,handleChange 都没有被调用。遗憾。 - jdarling
16
尝试在您的输入上使用defaultChecked={this.state.complete}而不是"checked"。 - zbyte
就是这样... 搜索了很久并四处探索。如果其他人也遇到这个问题,我会在问题中更新完整的工作答案。 - jdarling
但是为什么-遇到相同的问题,但你应该使用checked来控制组件 :/ - Dominic
6
checked 设置为 true 表示该状态是在组件外部进行管理的。当用户单击时,没有任何内容调用 handleChange 因为状态没有被更新。相反,您需要侦听 onClick 事件并在那里触发状态更新。 - zbyte

53

在这种情况下最好不要使用引用(refs),可以使用:

<input
    type="checkbox"
    checked={this.state.active}
    onClick={this.handleClick}
/>

有一些选项:

checkeddefaultChecked 的区别

前者会响应状态更改点击操作。 后者将忽略状态更改。

onClickonChange 的区别

前者始终会在点击时触发。 如果input元素上存在checked属性,后者将不会在点击时触发。


9
我正在使用React 16.13.1版本,如果没有onChange属性,就不能提供checked属性。 如果我同时定义了这两个属性,并且使checked属性对输入做出响应,则可以获得所需的行为,并且每次单击框时都会触发onChange事件。 因此,我认为此答案已过时。 - Ian
这个想法拯救了我在调试中的生命。 - Mickey

25

如果你有一个像这样的handleChange函数:

handleChange = (e) => {
  this.setState({
    [e.target.name]: e.target.value,
  });
}

您可以创建自定义的 onChange 函数,使其像文本输入框一样运作:

<input
  type="checkbox"
  name="check"
  checked={this.state.check}
  onChange={(e) => {
    this.handleChange({
      target: {
        name: e.target.name,
        value: e.target.checked,
      },
    });
  }}
/>

“handleChange” 在 “input” 上不应该是 “this.handleChange” 吗? - Ardhi
你犯了一个小错误 -> [e.target.name]: e.target.checked - Haikel
4
如果你有一个只处理复选框输入的handleChange函数,那么这是正确的。但是,如果你注意到第二个代码块,我们在箭头函数中调用handleChange并将target.value设置为e.target.checked(本质上是创建了一个伪事件)。这样做是为了使相同的handleChange函数也能处理文本输入,因为这是它们在更改事件中传递其值的方式。 - spencer.sm

15

如果您不想在输入DOM上使用onChange处理程序,可以使用onClick属性作为替代方法。在v16中,defaultChecked可能会让条件保持固定状态。

 class CrossOutCheckbox extends Component {
      constructor(init){
          super(init);
          this.handleChange = this.handleChange.bind(this);
      }
      handleChange({target}){
          if (target.checked){
             target.removeAttribute('checked');
             target.parentNode.style.textDecoration = "";
          } else {
             target.setAttribute('checked', true);
             target.parentNode.style.textDecoration = "line-through";
          }
      }
      render(){
         return (
            <span>
              <label style={{textDecoration: this.props.complete?"line-through":""}}>
                 <input type="checkbox"
                        onClick={this.handleChange}
                        defaultChecked={this.props.complete}
                  />
              </label>
                {this.props.text}
            </span>
        )
    }
 }

我希望这对未来的某个人有所帮助。


13

如果有人正在寻找通用的事件处理程序,下面的代码可以更多或更少地使用(假设对于每个输入都设置了name属性):

    this.handleInputChange = (e) => {
        item[e.target.name] = e.target.type === "checkbox" ? e.target.checked : e.target.value;
    }

3

当使用defaultChecked时,在移动设备上,onChange将不会调用handleChange。作为替代,您可以使用onClick和onTouchEnd。

<input onClick={this.handleChange} onTouchEnd={this.handleChange} type="checkbox" defaultChecked={!!this.state.complete} />;

1
在 Material UI 中,复选框的状态可以通过以下方式获取:
this.refs.complete.state.switched

0

对于未来遇到这个问题的任何人。我的问题是在标签的htmlFor中引用了输入name而不是id

因此,更改为:

<input type="checkbox" name="test" value="isGood" onChange={() => console.log('called')} />
<label htmlFor="test" >Is Good?</label>

到这里

<input type="checkbox" name="test" id="test" value="isGood" onChange={() => console.log('called')} />
<label htmlFor="test" >goood</label>

问题已修复


0

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