【问题标题】:Handling impure DOM manipulation after redux state change在 redux 状态更改后处理不纯的 DOM 操作
【发布时间】:2016-10-15 03:14:24
【问题描述】:

我有一个接口,它是<input>s 的列表。在其中一个输入中按回车键应将焦点移至下一个输入,如果您已经在最后一个输入上,则应添加另一个输入并聚焦。项目列表由 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 状态发生变化,并且列表会使用最后一个项目重新呈现。

我能想到的唯一方法是为每个列表项存储一个 ref 数组,然后在按下 enter 时调度 redux 事件并将焦点列表项的索引设置为组件状态。然后,在 componentDidUpdate 中(将在 redux 状态更新并且额外的输入在 DOM 中后调用),检查状态以查看我们应该关注哪个输入,并对其调用 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/

【问题讨论】:

    标签: javascript reactjs redux react-redux


    【解决方案1】:

    与其像上面那样以编程方式聚焦并在 List 中使用 state,不如将​​ prop 传递给 ListItem,如果 prop 为真,则列表项本身可以聚焦自己。因此,您也可以稍微更改您的操作以包含 isFocused 或 not 标志,例如:

    {
      type: 'ADD_ITEM'
      isFocused: true
    }
    
    {
      type: 'UPDATE_ITEM',
      index: i,
      value: value,
      isFocused: false/true
    }
    

    这似乎更有效,但使用 props 而不是 state 会更容易测试和推理。

    【讨论】:

    • ListItem 应该在什么方法中关注自身? isFocused 标志在获得焦点后是否会取消设置?
    • 它应该在 componentDidMount 和 componentDidUpdate 中(他们可以调用一个方法,这样你就不会重复相同的代码两次),本质上类似于 if (this.props.isFocused) this。输入焦点。
    • 聚焦后不需要取消flag,基本上让props成为真相的来源,你只需要确保只有1个ListItem的isFocused为true。
    • 除非该标志未设置,否则只要组件更新,即使用户已将焦点移至其他位置,最后一个焦点输入也会重复获得焦点。一旦输入聚焦一次,即使组件更新,也不应该再次强制聚焦。我做了你在这里的建议:jsfiddle.net/24mwqot2/1。请注意,当您在任何字段中键入时,最后一个强制聚焦的输入是如何聚焦的。
    • 让我看看你的 jsfiddle
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-04
    • 2022-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 2021-03-29
    相关资源
    最近更新 更多