在redux状态改变后处理不纯的DOM操作



我有一个接口,它是<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
}
使用props而不是state会使测试和推理更容易。

相关内容

  • 没有找到相关文章

最新更新