复选框内部状态覆盖道具

checkbox internal state overriding prop

我很惊讶复选框功能不是我所期望的。如果我有一个带有 onClick 的包装器 div,然后单击除复选框之外的任何内容,它会按预期工作。但是,当我单击该复选框时,即使传递下来的 propp 确实更新了,该复选框仍保持相同状态。我的猜测是,在 prop 更改被按下后,复选框正在触发其内部更新事件。

const ToDo = (props) => {
  const { title, done, onChange } = props;

  const handleChange = (event) => {
    event.preventDefault();
    if (onChange) {
      onChange(!done)
    }
  }

  return (
    <div onClick={handleChange}>
      <label>
        <input type="checkbox" checked={done}/> {title}
      </label>
    </div>
  );
};

我想知道处理这个问题的正确方法是什么?

这是我发现的简单且有效的解决方法。传递不同的键 key={done} 将强制将复选框组件重新呈现为不同的 dom 项目,因此它有效,但似乎应该有更好的方法来做到这一点。我知道在使用文本输入时不需要这样做,因为无论 prop 传递什么值,值都会保持不变。

const ToDo = (props) => {
  const { title, done, onChange } = props;

  const handleChange = (event) => {
    event.preventDefault();
    if (onChange) {
      onChange(!done)
    }
  }

  return (
    <div onClick={handleChange}>
      <label>
        <input key={done} type="checkbox" checked={done}/> {title}
      </label>
    </div>
  );
};

似乎发生的事情是,当您单击标签时,它会调用复选框的 onChange 函数以及 div 的 onClick

解决此问题的一个简单方法是禁用标签上的指针事件,因此仅调用 onClick(我假设出于某种原因您需要在父对象上使用 onClick 而不是onChange 输入本身)。

可能还有另一种方法可以阻止事件向上传播,但是你必须处理多个处理程序,所以 css 方法似乎很容易解决。

这是一个演示:

class App extends React.Component {
  state = {
    done: false
  }
  
  onCheckDone = (done) => {
    this.setState({
      done
    });
  }
  
  render() {
    return (
      <ToDo
        title="hello"
        done={this.state.done}
        onChange={this.onCheckDone}
      />
    );
  }
}

const ToDo = (props) => {
  const { title, done, onChange } = props;

  const handleChange = (event) => {
    event.stopPropagation();
    if (onChange) {
      onChange(!done)
    }
  }
  
  return (
    <div className="clicker" onClick={handleChange}>
      <label>
        <input type="checkbox" checked={done} /> {title}
      </label>
    </div>
  );
};

ReactDOM.render(
  <App />,
  document.getElementById('app')
);
.clicker {
  border: solid 1px #ccc;
  padding: 10px 20px;
}

label {
  pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>