ReactJS输入框在按键后失去焦点

3
使用哈希表存储动态创建的输入行的值,当我修改某个输入框后仅输入一个字符即失去焦点。我认为解决方案可能是使用refs重新聚焦于最后一个更改的输入框,但我无法让其起作用,因为我无法确定哪个元素是最后更改的。欢迎提供解决此问题的建议。
以下代码动态创建输入框,并根据unitPriceValueHash查找它们的值。每个变量都有一个id,id被用作哈希表的键。
我创建了一个CodePen来尝试重现这个问题,但是我面临的问题在CodePen中没有显示出来。在我的实际应用程序中,例如我按下1,然后光标不再在输入框上。

https://codepen.io/ByteSize/pen/oogLpE?editors=1011

唯一的区别在于codepen和我的代码之间,似乎是输入框嵌套在表格中。

Inputs nested in table

  CreateItem(variant) {
    const unitPriceValueHash = this.props.unitPriceValueHash
    return { 
      variant_title: variant.variant_title,
      variant_price: variant.variant_price,
      unit_cost: <TextField 
                  type="number"
                  onChange={(event) => this.handleUnitPriceChange(variant.id, event)}
                  key={variant.id}
                  value={unitPriceValueHash[variant.id] || ''}
                 />
    };
  }

以下是修改哈希值的状态更改。
  handleUnitPriceChange (id, event) {
    const unitPriceValueHash = this.state.unitPriceValueHash
    unitPriceValueHash[id] = event
    console.log(unitPriceValueHash)
    this.setState({unitPriceValueHash: unitPriceValueHash});
    //this.updateVariantUnitCost(id, event);
  }
2个回答

1

你分享的代码存在一些问题。

  • 不要使用内联函数。每次渲染,函数都会被重新创建,这意味着当React比较props时,它会认为函数是不同的(因为每次都是一个新的/不同的函数!),从而导致React重新渲染。
  • 不要修改状态中已经存在的任何对象,而是创建一个新的对象。如果你修改了状态中已经存在的对象,实际上就是在说你不希望渲染结果具有一致性和可重复性。

我已经重新发布了你的原始代码,并突出显示了这些问题。

CreateItem(variant) {
  const unitPriceValueHash = this.props.unitPriceValueHash
  return {
    variant_title: variant.variant_title,
    variant_price: variant.variant_price,
    unit_cost: <TextField
      type="number"
      onChange={(event) => this.handleUnitPriceChange(variant.id, event)}
      // ^^^^ - inline functions cause react to re-render every time, instead - create a component
      key={variant.id}
      value={unitPriceValueHash[variant.id] || ''}
    />
  };
}

handleUnitPriceChange(id, event) {
  const unitPriceValueHash = this.state.unitPriceValueHash
  unitPriceValueHash[id] = event
  // ^^^^ - please, please - don't do this. You can't mutate the state like this.

  // instead, do the following to create a new modified object without modifying the object in the state
  const unitPriceValueHash = Object.assign({}, this.state.unitPriceValueHash, { id: event });
  this.setState({ unitPriceValueHash: unitPriceValueHash });
}

关于内联函数,通常建议创建一个新的组件,并将该值作为属性传递。可能会像这样:

class UnitCost extends PureComponent {
  static propTypes = {
    variantId: PropTypes.number,
    variantValue: PropTypes.object,
    onUnitPriceChange: PropTypes.func,
  }
  handleUnitPriceChange(e) {
    this.props.onUnitPriceChange(this.props.variantId, e)
  }
  render() {
    return (
      <TextField
        type="number"
        onChange={this.handleUnitPriceChange}
        value={this.props.variantValue || ''}
      />
    );
  }
}

CreateItem(variant) {
  const unitPriceValueHash = this.props.unitPriceValueHash
  return {
    variant_title: variant.variant_title,
    variant_price: variant.variant_price,
    unit_cost: (
      <UnitCost
        key={variant.id}
        variantId={variant.id}
        variantValue={unitPriceValueHash[variant.id]}
        onUnitPriceChange={this.handleUnitPriceChange}
      />
    ),
  };
}

关于您对焦点的担忧,React通常不会在重新渲染时丢失对象的焦点,因此基于此原因,请绝对不要在更新后重新聚焦对象。
唯一情况下React会丢失焦点是,如果它完全丢弃当前的DOM树并从头开始重建。如果它认为父对象已被替换而不是修改,就会这样做。这可能是由于缺少key属性或更改了key属性导致的。
您没有发布足够的代码供我们进一步调查。如果您想获得更多帮助,应该构建一个最小可重现的示例,以便我们可以运行和测试。

Caesay,谢谢您的见解!我正在努力构建一个 Codepen 来复现这个问题。 - ByteMe
与此同时,我已经发布了完整的代码视图。由于许多内容(例如变量)来自Rails后端,因此创建Codepen需要一些时间。 - ByteMe

1
这个问题的解决方案是在输入框改变时使用一个中间状态来存储输入字段的值,并在失去焦点时提交一个 AJAX 请求。
class TextFieldWrapper extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: this.props.variantValue[this.props.variantId] || '',
    }
    this.handleUnitPriceChange = this.handleUnitPriceChange.bind(this)
    this.updateValue = this.updateValue.bind(this)
  }
  updateValue(value){
    this.setState({
      value: value,
    });
  }
  handleUnitPriceChange() {
    this.props.onUnitPriceChange(this.props.variantId, this.state.value);
  }
  render(){
    return (
      <TextField
        type="number"
        id={this.props.variantId}
        key={this.props.variantId}
        onChange={this.updateValue}
        onBlur={this.handleUnitPriceChange}
        value={this.state.value}
      />
    );
  }
}

2
不要在 props 中内联使用 .bind(this) - 它会遇到我在答案中提到的同样的问题(它会在每次渲染时创建一个新函数)。最好在构造函数中执行此操作。 - caesay

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接