我有一个接口,它是<input>
的列表。在其中一个输入中按下回车键应该会将焦点移到下一个输入,如果你已经在最后一个输入中,应该添加另一个输入并聚焦那个输入。项目列表由redux管理。
下面的代码完成了这部分,没有聚焦:
class ListItem extends Component {
render() {
return (
<li>
<input
defaultValue={this.props.value}
onKeyUp={this.onKeyUp.bind(this)}
ref={(input) => this.input = input}
/>
</li>
);
}
onKeyUp(event) {
this.props.onChange(event.target.value, event.keyCode === 13);
}
focus() {
this.input.focus();
}
}
class List extends Component {
render() {
return (
<ul>
{this.props.items.map((item, i) =>
<ListItem
key={i}
value={item}
onChange={(value, enterPressed) => this.onChange(i, value, enterPressed)}
/>
)}
</ul>
);
}
onChange(i, value, enterPressed) {
this.props.dispatch({
type: 'UPDATE_ITEM',
index: i,
value: value
});
if (enterPressed && i === this.props.items.length - 1) {
this.props.dispatch({
type: 'ADD_ITEM'
});
}
}
}
这看起来真的很棘手,因为当我们想要关注下一个列表元素时,它可能还不存在,所以我们需要等待,直到redux状态发生变化,并且列表呈现最后一项。
我能想到的唯一方法是存储每个列表项的引用数组,然后在按enter键时调度redux事件并设置焦点列表项在组件状态下的索引。然后,在componentDidUpdate(它将在redux状态得到更新并且额外的输入在DOM中之后被调用)中,检查state以查看我们应该关注哪个输入,并对其调用focus方法。
class List extends React.Component {
constructor(props) {
super(props);
this.inputs = [];
}
render() {
return (
<ul>
{this.props.items.map((item, i) =>
<ListItem
key={i}
value={item}
onChange={(value, enterPressed) => this.onChange(i, value, enterPressed)}
ref={(input) => this.inputs[i] = input}
/>
)}
</ul>
);
}
onChange(i, value, enterPressed) {
this.props.dispatch({
type: 'UPDATE_ITEM',
index: i,
value: value
});
if (enterPressed) {
this.setState({
inputToFocus: i + 1
})
if (i === this.props.items.length - 1) {
this.props.dispatch({
type: 'ADD_ITEM'
});
}
}
}
componentDidUpdate() {
if (typeof this.state.inputToFocus === 'number') {
this.inputs[this.state.inputToFocus].focus();
this.setState({
inputToFocus: null
});
}
}
这感觉真的很脆弱和丑陋。有更好的方法吗?
完整的示例在https://jsfiddle.net/69z2wepo/59903/.
与其像上面那样编程地聚焦并在列表中使用state
,不如将一个prop传递给ListItem,如果prop为真,列表项本身可以聚焦。所以你也可以稍微改变你的动作,包括isFocused或not标志,例如:
{
type: 'ADD_ITEM'
isFocused: true
}
{
type: 'UPDATE_ITEM',
index: i,
value: value,
isFocused: false/true
}