tl;dr React 拒绝尊重checked={checkThisOption}
输入,即使它data-ischecked={checkThisOption}
完全尊重同一组输入。
我还没有在 jsfiddle 上做过这项工作,但我已经使用这段代码重现了这个问题。
长版
我有一个简单的 ReactJS 组件,可以向用户显示单选按钮列表。用户应该能够选择一个收音机,然后按下一个按钮来确认他们的选择。
这是组件定义(注意:我使用的是 ES6 和 webpack):
import React from 'react';
class Widget extends React.Component {
constructor(props) {
super(props);
this.state = {
currentValue: null // tracks currently-selected choice, by its value
};
}
onClickOptionRadio = (event) => {
this.setState({
currentValue: String(event.currentTarget.value)
});
}
onConfirm = (event) => {
if(!this.props.onChange) return;
this.props.onChange(this.state.currentValue);
};
render() {
let choices = this.props.choices;
let currentValue = this.state.currentValue;
return (
<div className="Widget">
<ol className="choices">
{
choices.map((choice, i) => {
// decide whether to mark radio as checked:
// - if no current choice, check first radios
// - otherwise, check radio matching current choice
let noCurrentChoice = (currentValue === null);
let drawingFirstChoice = (i === 0);
let thisChoiceIsSelected = (String(choice.value) === currentValue);
let checkThisOption = thisChoiceIsSelected || (noCurrentChoice && drawingFirstChoice);
return (
<li key={i}>
<input type="radio" name="choices"
value={choice.value}
onChange={this.onClickOptionRadio}
checked={checkThisOption?'checked':''}
data-ischecked={checkThisOption}
/>
<label>{choice.label}</label>
{' '}
{checkThisOption ? 'CHECKED' : ''}
</li>
);
})
}
</ol>
<button onClick={this.onConfirm}>Confirm choice</button>
</div>
);
}
}
export default Widget;
这是拥有的组件:
import React from 'react';
import Widget from 'components/widget';
class Owner extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
let choices = [
{ value: 10, label: 'First' },
{ value: 20, label: 'Second' },
{ value: 30, label: 'Third' }
];
return (
<div className="Owner">
<Widget
choices={choices}
/>
</div>
);
}
}
export default Owner;
这是它在行动中的 gif:
请注意视频中的几点:
- 该逻辑显然适用于在初始渲染时检查第一个收音机
- 当用户点击其他收音机时,它们不会被选中
- 但是,该逻辑显然适用于识别选择了哪个项目,如
margin-right: 2rem
应检查的收音机所示 - 指示已选择哪个选项的文本自始至终都是准确的
- 当我单击收音机时,该
componentWillUpdate
方法仅针对 Widget 本身触发;它的祖先都没有更新
我认为这个演示证明这不是Widget
实例被另一个状态为空的实例替换的情况。当前选择由input 上的data-
attr以及纯文本准确反映的事实表明状态按需要持续存在。我确信这种不受欢迎的行为是设计使然,我想知道如何解决 React 应用于受控输入的表单相关属性的奇怪的异常逻辑。
为什么我认为当前的行为是错误的?我不希望拥有的组件知道每个单选按钮——所有者应该绑定到 Widget 的onChange
方法,以便在做出最终选择后得到通知。
这是一个简化的例子。真正的组件更复杂,但原理是一样的:就像一个日期选择组件可能有很多自己的组件不知道的内部状态(比如显示什么时间尺度,哪年、哪月、哪周显示)显示等),这个组件也有一些有趣的内部状态,拥有组件没有业务管理。
据我所知,我已经完全正确地做到了这一点。组件通过 发布其重要的状态更新onChange(event, newValue)
,拥有的组件应该绑定到。我认为很明显 React决定不更新checked
这些输入的attr,即使它显然能够更新相同元素上的其他 attr 以响应相同的用户操作。
需要注意的是所有者不当前监听onChange
,但是这不应该事:孩子组件应该能够管理,即使业主不听自己的内部状态。并且我拒绝仅仅因为 Owner 没有提供currentValue
via props就认为无线电状态不能准确的断言:Widget 在没有那个 props 的情况下直接管理和呈现它的状态。React 必须做一些特殊的事情,以防止checked
根据适用于所有其他元素和属性的规则进行处理。这是一个例外,我认为这是一个糟糕的例外。
最后,请注意,此问题似乎仅在此组件低于某个 comp-tree 深度时才会发生。当它是 Flux“页面”或 Redux“容器”中的唯一组件时,它工作得很好。当它嵌套得更深时,就会如我所描述的那样失败。我还没有想出一个简洁的方式来证明这一点。
任何建议表示赞赏。据我所知,这里 React 违反了它自己规定的规则,我希望这种行为会阻碍构建其他围绕 vanilla 构建的有状态组件input
。
编辑:我更正了name
为收音机生成的s,并更新了演示以反映它。向任何开始追逐这些东西的人道歉。