Controlled inputs and composite types

- 1 answer

Ad

It is easy to create a form with controlled inputs when all the model fields are scalar and mapped 1:1 to the inputs.

But what if the field is composite? For certainty let's say that the model has a coordinates property that looks like this:

{
    ...
    coordinates: {
        x: 10,
        y: 20,
        projection: 1234
    }
}

And let's say in the form there is a single text input that is used to manage this field.

So, I can easily initialise the input with serialised point as (x, y) (projection cannot be modified).

But as the controlled inputs mutate the model on every change - there arises the problem: what if the input does not contain a valid serialised point? Like foobar.

This string cannot be deserialised back to the x-y-projection and it's not clear where to store this intermediate invalid result, since the controlled element triggers the model change and re-rendering.

So my question - how to represent the data in this case and how it is usually resolved in your apps?

Ad

Answer

Ad

You use state. The input component should only message its parent about a change in value once it has a valid value. You can set the child components state, to track the changing invalid values, then message the parent once you are done. It might look something like this. (here is a working codepen, note how the label doesn't change if you type letters into the input)

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>
    );
  }
}
Ad
source: stackoverflow.com
Ad