尝试创建一个受控的切换器按钮

Trying to create a controlled switcher button

我在网上找到了这段代码(它在 google 结果中排名靠前): https://codepen.io/BuiltByEdgar/pen/jWOVYQ

但我对它的代码感到厌烦。

我正在尝试将这些按钮设置为默认状态为开启状态,不允许同时关闭两个按钮。我为此选择的 UI 行为是简单地重新启用另一个,如果它的兄弟姐妹被关闭:

onONEClick() {
    let {
        TWO,
        ONE,
    } = this.state;

    if (TWO) {
        ONE = !ONE;
    } else if (ONE) {
        TWO = !TWO;
        ONE = !TWO;
    } else {
        ONE = !ONE;
    }

    this.setState({
        ONE,
        TWO,
    });
    this.props.callBack({
        ONE,
        TWO,
    });
}
onTWOClick() {
    let {
        TWO,
        ONE,
    } = this.state;

    if (ONE) {
        TWO = !TWO;
    } else if (TWO) {
        TWO = !TWO;
        ONE = !ONE;
    } else {
        TWO = !TWO;
    }

    this.setState({
        ONE,
        TWO,
    });
    this.props.callBack({
        ONE,
        TWO,
    });
}

和渲染:

     <div>
        <div className="switch-container">
            <label>
                <input
                    onChange={this.onONEClick}
                    type="checkbox"
                    className="switch"
                    value={this.state.ONE}
                    checked={this.state.ONE}
                />
                <div>
                    <span><g className="icon icon-toolbar grid-view" /></span>
                    <span><g className="icon icon-toolbar ticket-view" /></span>
                    <div />
                </div>
            </label>
        </div>
        ONE
    </div>
    <div>
        <div className="switch-container">
            <label>
                <input
                    onChange={this.onTWOClick}
                    type="checkbox"
                    className="switch"
                    value={this.state.TWO}
                    checked={this.state.TWO}
                />
                <div>
                    <span><g className="icon icon-toolbar grid-view" /></span>
                    <span><g className="icon icon-toolbar ticket-view" /></span>
                    <div />
                </div>
            </label>
        </div>
        TWO
    </div>

够简单了吧?

问题是反应讨厌我的切换器是 "checkbox" 类型的输入:

如您所见,我已尽最大努力避免这种情况发生,但我认为除了人工之外,不可能拥有输入类型并更改其检查状态。

所以我在想也许我可以通过仅使用 div 重建复选框来解决问题。 (label标签也违反了ESLint)

JLAITIO 回答:

这是我添加的内容(文件的顶部带有 props 声明和构造函数):

const propTypes = {
    value: React.PropTypes.object,
    type: React.PropTypes.string,
    callBack: React.PropTypes.func,
    ONE: React.PropTypes.bool,
    TWO: React.PropTypes.bool,
};

const defaultProps = {
    callBack: () => {},
    onChange: () => {
    },
    value: { type: '' },
    ONE: true,
    TWO: true,
};



class ProviderInfosComponent extends Component {
    constructor(props) {
        super(props);
        const initialValue = props.value;
        this.state = {
            ...initialValue,
            ONE: this.props.ONE,
            TWO: this.props.TWO,
        };
        this.onONEClick = this.onONEClick.bind(this);
        this.onTWOClick = this.onTWOClick.bind(this);
    }

如果在 any 点你的 value 属性没有设置,或者设置为 undefined 的值,然后随后它是一个现有的 React 状态值。

我最好的猜测是您的构造函数没有初始化 ONETWO,因此开始时的值为 undefined。然后,当您实际拥有一个值时,该组件从不受控制的 (="component state independent of React") 转移到受控的 (="component state tied directly to React state").

作为对@jlaitio 回答的补充,

您不必为复选框输入设置值。您可以只使用组件的 checked 道具。此外,对更改后的函数进行小幅改进可能有助于更轻松地跟进这些值。

  constructor(props) {
    super(props);
    this.state = {
      ONE: true,
      TWO: true
    };
  }
  onONEChange = (event) => {
    this.setState((prevState) => {
      ONE: event.target.checked,
      TWO: (event.target.checked === false ? true : prevState.TWO) // change state of TWO if ONE is false else just leave it as is
    }, () => {
        this.props.callBack({ ONE: this.state.ONE, TWO: this.state.TWO });
    });
  }
  onTWOChange = (event) => {
    this.setState((prevState) => {
      ONE: (event.target.checked === false ? true : prevState.ONE), // change state of ONE if TWO is false else just leave it as is
      TWO: event.target.checked
    }, () => {
        this.props.callBack({ ONE: this.state.ONE, TWO: this.state.TWO });
    });
  }