Javascript 受控输入和复合类型

Javascript 受控输入和复合类型,javascript,reactjs,Javascript,Reactjs,当所有模型字段都是标量并以1:1的比例映射到输入时,很容易创建具有受控输入的表单 但如果场是复合场呢?为了确定起见,我们假设模型有一个坐标属性,如下所示: { ... coordinates: { x: 10, y: 20, projection: 1234 } } 假设表单中有一个文本输入,用于管理这个字段 因此,我可以轻松地用序列化点初始化输入,如(x,y)(投影无法修改) 但是,随着受控输入在每次更改时对模型进行变异

当所有模型字段都是标量并以1:1的比例映射到输入时,很容易创建具有受控输入的表单

但如果场是复合场呢?为了确定起见,我们假设模型有一个
坐标
属性,如下所示:

{
    ...
    coordinates: {
        x: 10,
        y: 20,
        projection: 1234
    }
}
假设表单中有一个文本输入,用于管理这个字段

因此,我可以轻松地用序列化点初始化输入,如
(x,y)
(投影无法修改)

但是,随着受控输入在每次更改时对模型进行变异,出现了一个问题:如果输入不包含有效的序列化点怎么办?像
foobar

此字符串无法反序列化回
x-y-projection
,并且不清楚在何处存储此中间无效结果,因为受控元素会触发模型更改和重新渲染


因此,我的问题是——在这种情况下如何表示数据,以及它通常在应用程序中是如何解决的?

不取决于您如何保存数据(状态/无状态),您可以将无效数据存储在状态/存储中,并在组件的呈现方法中相对地处理它


任何附加的从句都可以得到一个更加清晰和完整的答案

您使用的状态。输入组件只有在具有有效值后才应向其父组件发送有关值更改的消息。您可以设置子组件状态,以跟踪更改的无效值,然后在完成后向父组件发送消息。它可能看起来像这样。(这是一个工作示例,请注意,如果在输入中键入字母,标签将不会改变)

类PointInput扩展React.Component{
构造函数(道具,…参数){
超级(道具,…args);
this.state={x:props.x,y:props.y};
}
更新=(新值)=>{
newValue=Object.assign({},this.state,newValue);
此.setState(newValue);
if(Number.isNaN(+newValue.x)| Number.isNaN(+newValue.y))返回;
this.props.onChange(newValue);
}
render(){
返回(
{'X:'}this.update({X:e.target.value})}/>
{'Y:'}this.update({Y:e.target.value})}/>
);
}
}

我也有同样的想法,尽管它看起来像是一个“解决方案”,但它也有一个“问题”:受控元素带来的好处是,您看到的是商店中现在的东西不再有效。因此,如果用户单击“提交”,则不是将要发送的输入字段中的数据,而是可能不直观的“最后有效数据”。这可以通过使用额外的客户端验证来解决(虽然这可能很棘手,因为现在不仅要验证存储,还要验证内部组件的状态),您开始引入其他问题,这必然会改变行为。有几种方法可以实现这一点。验证可以在存储级别完成,但也可以由组件完成(可能是在值无效时返回
null
值,从而导致存储验证失败)。如果存储确实在任何时候都需要该值,那么可以将输入与解析的值分开。存储将包含原始输入的一个值,以及序列化点的一个值(原始输入有效后更新)。然后输入总是报告,并忽略有效性。
class PointInput extends React.Component {
  constructor(props, ...args) {
    super(props, ...args);
    this.state = { x: props.x, y: props.y};
  }
  update = (newValue) => {
    newValue = Object.assign({}, this.state, newValue);
    this.setState(newValue);
    if (Number.isNaN(+newValue.x) || Number.isNaN(+newValue.y)) return;
    this.props.onChange(newValue);
  }
  render() {
    return (
      <div>
        {'X: '}<input value={this.state.x} onChange={(e) => this.update({x: e.target.value})} />
        {'Y: '}<input value={this.state.y} onChange={(e) => this.update({y: e.target.value})} />
      </div>
    );
  }
}