What are the consequences of mutating event.target.value (in React)?

Viewed 44

Let's say we are building custom input component.

In this component, as an example let's say we want to change the value from a string to a number

const CustomInputComponent = ({ onChange, ...rest }) => {
  const onChangeHandler = (event)=>{
    // What are the consequences of doing this?
    event.target.value = parseInt(event.target.value, 10);
    onChange(event);
  }
  return <input type="text" onChange={onChangeHandler} />
}

What are the consequences of directly mutating event.target.value like this?

1 Answers

What are the consequences of directly mutating event.target.value like this?

They're exactly what you would expect: the value of the input gets changed to the new value.

Granted, if you assume that the user enters a numeric value, there won't be any noticeable output - because although you're overwriting the event.target.value from say the string "1" to the number 1, that will get coerced back to a string inside the input, because input values can only ever be strings.

But, if the user types anything else, you'll get strange-looking - and I can only assume unintended - consequences. See the demo below where I've set up a simple render of your component (with an onChange function prop that does nothing - this is just to illustrate your component without it crashing and makes no difference here), and observe for yourself the consequences (spelled out below the snippet).

const CustomInputComponent = ({ onChange, ...rest }) => {
  const onChangeHandler = (event)=>{
    // What are the consequences of doing this?
    event.target.value = parseInt(event.target.value, 10);
    onChange(event);
  }
  return <input type="text" onChange={onChangeHandler} />
}

const App = () => <CustomInputComponent onChange={()=>{}} />;

ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

  • as previously observed, if you enter the character "1", it gets displayed as expected.
  • if you then enter a non-numeric character, it won't appear at all. This is because the when you type "a" here, say, the event.target.value is "1a", and parseInt("1a", 10) is equal to 1. (parseInt is different from coercion via Number in that it will read whatever numeric values it can from the start of the string and just stop processing when it hits a character it can't parse.) So the value gets set to 1, the same as it was before.
  • if you enter any non-numeric content, including simply by deleting the above (to leave event.target.value as the empty string), or by resetting to the start and entering a non-numeric character, the output will become NaN. That's because strings which don't even begin with a numeric character, including the empty string, return NaN ("not a number", although confusingly that value has type number) when parseInt is used.

So no, you don't want to do this.

The only value - assuming it's acceptable to you for things to go so wonky as this with non-numeric input - seems to be to allow consumers of this prop to provide an onChange which directly acts on numbers without having to convert from string to number inside the function itself. This seems an incredibly weak benefit to me, for the following reasons:

  1. your component is an <input type="text" /> without even an inputmode="numeric" to suggest numeric input is required. There's absolutely no reason to expect users won't want to type non-numeric characters here - and even if you give it a label and description that make abundantly clear only numbers are allowed, users are still going to do it (if only by committing typos), and expect the input to appear. You want to give an error message in these circumstances, not force the input to change it to something else.

  2. even if this is supposed to be a numeric input, anyone with any experience of developing with HTML and Javascript knows full well that event.target.value is always a string. Even if they want to do numeric things with that string, they'll fully expect to have to do the conversion to a number themselves.

  3. if despite the above, you really want to provide an event object to the onChange function prop which has a number for it's target value, you can do it without mutating the existing event object, for example like this:

const modifiedEvent = { ...event, { target: { ...event.target, value: parseInt(event.target.value, 10) }}};
onChange(modifiedEvent);

And well I find that line of code kind-of disgusting - although you can probably make it less horrendous, perhaps with some helper libraries that are specifically designed for immutable "updates" like this. But for me really it's yet another reason, if one were really needed, not to do this. Having said that, even the above is much better than the direct mutation of event.target.value that you're asking about.

Related