Version: 3.9.0 — src/lib/form/bignumber-field/useBigNumberField.tsx
Four related behaviours make a controlled BigNumberField unable to express "empty", so consumers
resort to remounting the field and reading the raw input text (kleros/kleros-v2#2281:
web/src/hooks/useBigNumberFieldReset.ts, Resolver/Landing/CreationCard.tsx, the stake and
appeal amount fields).
- An emptied input is reported as
0. handleChange: if (newInputValue === "") { …; onChange?.(new BigNumber(0)) }.
The consumer cannot tell "cleared" from a typed 0 (which is a valid dispute ID, for example).
value="" renders NaN. The initial/sync path does new BigNumber(value.toString()) → NaN → toFormat() → "NaN".
value={undefined} is ignored. The sync effect is if (value !== undefined) { setInputValue(value.toString()) … },
so once a value has been shown there is no prop that empties the field again (e.g. resetting an amount after a
transaction popup closes).
- The sync effect rewrites the text on every numeric change. With
value={amount} onChange={v => setAmount(v.toString())}
each keystroke echoes a new value and setInputValue(value.toString()) replaces what the user typed: 1.05 →
Backspace → 1 (not 1.0), so typing 3 yields 13. Holding a BigNumber in state is worse (new object each
render → cannot type 1. at all).
Proposal
- Model empty explicitly:
onChange(null) when the input is emptied (type (value: BigNumber | null) => void), and
accept value={null} / value="" to clear the input.
- In the sync effect, skip when the incoming value is numerically equal to the current
numberValue
(new BigNumber(value).isEqualTo(numberValue)), so the field's own onChange echo is a no-op and in-progress text
such as 1.0 survives.
Version: 3.9.0 —
src/lib/form/bignumber-field/useBigNumberField.tsxFour related behaviours make a controlled
BigNumberFieldunable to express "empty", so consumersresort to remounting the field and reading the raw input text (kleros/kleros-v2#2281:
web/src/hooks/useBigNumberFieldReset.ts,Resolver/Landing/CreationCard.tsx, the stake andappeal amount fields).
0.handleChange:if (newInputValue === "") { …; onChange?.(new BigNumber(0)) }.The consumer cannot tell "cleared" from a typed
0(which is a valid dispute ID, for example).value=""rendersNaN. The initial/sync path doesnew BigNumber(value.toString())→NaN→toFormat()→"NaN".value={undefined}is ignored. The sync effect isif (value !== undefined) { setInputValue(value.toString()) … },so once a value has been shown there is no prop that empties the field again (e.g. resetting an amount after a
transaction popup closes).
value={amount} onChange={v => setAmount(v.toString())}each keystroke echoes a new
valueandsetInputValue(value.toString())replaces what the user typed:1.05→Backspace →
1(not1.0), so typing3yields13. Holding aBigNumberin state is worse (new object eachrender → cannot type
1.at all).Proposal
onChange(null)when the input is emptied (type(value: BigNumber | null) => void), andaccept
value={null}/value=""to clear the input.numberValue(
new BigNumber(value).isEqualTo(numberValue)), so the field's ownonChangeecho is a no-op and in-progress textsuch as
1.0survives.