React复选框无法切换状态

34

当我点击复选框时,虽然onChange处理程序中的console.log表明状态已更改为false,但选中标记仍未消失。另一方面,当状态由单独的按钮更改时,选中标记可以正确地切换开和关。

export default class TestComponent extends Component {

constructor(props) {
    super(props);
    this.state = {
        is_checked: true
    };
    this.updateCheckbox = this.updateCheckbox.bind(this);
    this.pressButton = this.pressButton.bind(this);
}
updateCheckbox(event) {
    event.preventDefault();

    this.setState({is_checked: !this.state.is_checked});
    console.log(this.state.is_checked);  // This logs 'false' meaning the click did cause the state change 
    console.log(event.target.checked);  // However, this logs 'true' because the checkmark is still there 

}
pressButton(event){
    event.preventDefault();
    this.setState({is_checked: !this.state.is_checked});
}
render(){

    return (
   <input type="checkbox" onChange={this.updateCheckbox} checked={this.state.is_checked} ></input>
   <button  onClick={this.pressButton}>change checkbox state using button</button>
    );
}
}

我知道已经有一段时间了,但是被接受的答案有些误导(虽然不是完全错误)。此外,我认为你不应该像你自己的答案那样使用双向绑定来解决这个问题。 - user6445533
4个回答

41
我想我明白发生了什么。
当您点击按钮时,它会切换“is_checked”,这将选中或取消选中框。但是这最终会触发复选框的“onChange”,从而也会切换状态......实际上,您已经编写了一个无限循环。不过,由于React对“setState”操作进行了批处理/去抖动,您的代码不会锁定页面。
尝试这个:
2019用于Hooks API的更新:
import React, { useState } from 'react';

const Component = () => {
  const [isChecked, setIsChecked] = useState(true);

  return (
    <div>
      <input
        type="checkbox"
        onChange={(event) => setIsChecked(event.currentTarget.checked)}
        checked={isChecked}
      />
      <button onClick={() => setIsChecked(!isChecked)}>
        change checkbox state using this button
      </button>
    </div>
  );
};

Original:

var React = require("react");

var Component = React.createClass({
    getInitialState: function() {
        return {
            isChecked: true
        };
    },

    handleCheckboxChange: function(event) {
        console.log("checkbox changed!", event);
        this.setState({isChecked: event.target.checked});
    },

    toggleIsChecked: function() {
        console.log("toggling isChecked value!");
        this.setState({isChecked: !this.state.isChecked});
    },

    handleButtonClick: function(event) {
        console.log("button was pressed!", event);
        this.toggleIsChecked();
    },

    render: function() {
        return (
            <div>
                <input type="checkbox" onChange={this.handleCheckboxChange} checked={this.state.isChecked} />
                <button onClick={this.handleButtonClick}>change checkbox state using this button</button>
            </div>
        );
    }
});

module.exports = Component;

请注意,您可以使用React的valueLink(在此处阅读更多:https://facebook.github.io/react/docs/two-way-binding-helpers.html)使此代码更加简洁。


1
感谢您的帮助。您提供的代码仍然存在同样的问题,但是双向绑定的链接很有帮助。我更新了我的帖子以展示解决方案。 - Jason O.
我忘记在handleCheckboxChange函数中添加更新isChecked状态的代码行 - 秘诀是使用event.target.checked的值。 我已经更新了代码,仅供参考。 这里还有一个fiddle链接:https://jsfiddle.net/xqb2mxsq/ - Kabir Sarin
1
你也可以通过删除 "event.preventDefault();" 这一行来解决这个问题。因为它会阻止复选框被选中,从而始终保持相同的状态。 - Skäggiga Mannen
我知道这很老套,但问题的根源在于 e.preventDefault()。通过阻止 on change 的默认行为,即使事件处理程序被调用,您也会阻止更改发生。换句话说,复选框发生变化,状态发生变化,然后复选框取消设置自身,因为其行为被阻止了。因此,只需删除 e.preventDefault(),初始代码就可以正常工作。 - oooyaya
这对我使用 useReducer 也有效。感谢你。 - chidimo

19

这种行为的原因与React的实现细节有关 - 更具体地说,是React如何在不同浏览器中规范化处理变化。对于单选按钮和复选框输入,React使用点击事件代替更改事件。当你在附加的事件处理程序中应用preventDefault时,这会阻止浏览器在视觉上更新单选按钮/复选框输入。有两种可能的解决方法:

  • 可以选择使用stopPropagation
  • 将切换操作放入setTimeout中:setTimeout(x => this.setState(x), 0, {is_checked: !this.state.is_checked});

最好不要使用preventDefault,除非绝对必要。

请查看React Github问题以获取更多信息。


1
去掉preventDefault对我来说解决了问题,使用react 15和typescript 2.2(抱歉格式不好)。 toggleMyCheckbox(checkboxExent: React.ChangeEvent<HTMLInputElement>) { checkboxExent.preventDefault(); this.setState({ checkboxModel: this.state.checkboxModel, checked: !this.state.checked })}变成: toggleMyCheckbox(checkboxExent: React.ChangeEvent<HTMLInputElement>) { this.setState({ checkboxModel: this.state.checkboxModel, checked: !this.state.checked })} - JMStudios.jrichardson
谢谢,我也去掉了preventDefault,然后它对我起作用了。 - Box and Cox
讲解得非常好,真的帮助了这个 React 新手。 - Native Coder
谢谢,preventDefault() 也是我的问题。 - Beck Johnson

1
请记住,setState是异步的,因此:
 console.log(event.target.checked);

不应立即反映更改。我处理多个复选框字段的方法:

 toggleCheckbox(name, event) {
    let obj = {}; 
    obj[name] = !this.state[name];
    this.setState(obj);
 }

这些字段:

<input type="checkbox" name="active" value={this.state.active} checked={this.state.active} onChange={this.toggleCheckbox.bind(this, 'active')} />

<input type="checkbox" name="qtype" value={this.state.qtype} checked={this.state.qtype} onChange={this.toggleCheckbox.bind(this, 'qtype')} />

0

根据Kabir提供的链接,按照React文档中关于双向绑定的说明更改了代码。

为了使其正常工作,我不得不:

  1. 使用 "React.createClass" 代替 "extends Component",以便使用 LinkedStateMixin

    import react from 'react/addons'
    
    var TestComponent = React.createClass({
    mixins: [React.addons.LinkedStateMixin],
    render: function(){ ....
    
  2. 删除 onChange='updateCheckbox' 并使用 this.linkState。

    <input type="checkbox" checkedLink={this.linkState(this.state.is_checked)} ></input> 
    

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