What would be an idiomatic way to solve the following issue? Say I have a number input bound to some state:
@react.component
let make = () => {
let (increment, setIncrement) = React.useState(() => 1)
let onInputIncrement = e => {
ReactEvent.Form.target(e)["value"]
->Int.fromString
->Option.getWithDefault(increment)
->Function.const
->setIncrement
}
<>
<Counter increment />
<input
type_="number"
min="1"
step=1.0
value={increment->Int.toString}
onInput=onInputIncrement
/>
</>
}
This handling of the Option resulting from Int.toString
means that I can never backspace to delete the entire contents of the input. I want to be able to blank out the input while typing, with the value staying the same in the meantime. The only solution I’ve found for this (besides switching to onChange
) is to introduce a secondary cache state:
@react.component
let make = () => {
let (incrementValue, setIncrement) = React.useState(() => Some(1))
let (incrCache, setIncrCache) = React.useState(() => 1)
let increment = incrementValue->Option.getWithDefault(incrCache)
let onInputIncrement = e => {
let value = ReactEvent.Form.target(e)["value"]->Int.fromString
setIncrement(_ => value)
value->Option.mapWithDefault((), i => setIncrCache(_ => i))
}
<>
<Counter increment />
<input
type_="number"
min="1"
step=1.0
value={incrementValue->Option.mapWithDefault("", Int.toString)}
onInput=onInputIncrement
/>
</>
}
This is quite a tortuous pattern for something which could end up coming up a lot. Is there a better way?