I am new to TypeScript and currently I am designing a BMR calculator. I have created a handleNumericInput
function that only allows numbers to be accepted inside an input box but the problem is when I type a non-numeric character inside, it will either return NaN
or clear the field entirely (depending on how I have set the value for each input). I just want it to not be typed at all. Here is my code:
App.tsx
:
// Defined measurements and states
const [metricMeasurement, setMetricMeasurement] = useState<Metric>({
heightInCentimetres: 0,
weightInKilos: 0,
age: 0,
});
// US measurement state
const [usMeasurement, setUsMeasurement] = useState<US>({
heightInFeet: 0,
heightInInches: 0,
weightInPounds: 0,
age: 0
});
// handleNumericInput function
const handleNumericInput = (e: React.ChangeEvent<HTMLInputElement>,
measurementType: "metric" | "us"
) => {
const { name, value } = e.target;
if (/^\d*\.?\d*$/.test(value)) {
measurementType === "metric" ?
setMetricMeasurement((prev) => ({ ...prev, [name]: Number(value) })) :
setUsMeasurement((prev) => ({ ...prev, [name]: Number(value)}))
} else {
setMetricMeasurement((prev) => ({ ...prev, [name]: "" }))
setUsMeasurement((prev) => ({ ...prev, [name]: "" }))
}
};
Metric.tsx
:
<input
className='m-3 bg-slate-100 border w-full h-10 border-slate-300 rounded pl-1'
placeholder="cm"
inputMode="numeric"
type='text'
pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
value={metricMeasurement.heightInCentimetres || ''}
name="heightInCentimetres"
onChange={(e) => {
setMetricMeasurement({
...metricMeasurement,
heightInCentimetres: Number(e.target.value)
});
handleNumericInput(e, "metric");
}}
/>
If i use value={metricMeasurement.heightInCentimetres || ''}
and type a non-numeric character, it clears the field but if I just use value={metricMeasurement.heightInCentimetres}
and type a non-numeric character, it returns NaN
.
And yes, I am aware that you can use type="number"
to only allow numbers to be typed. I have set to text to allow placeholders to define measurement types. Any help will be appreciated.
You're updating the state in two places. I think you should remove one from onChange and update it only if the input is numeric:
<input
className='m-3 bg-slate-100 border w-full h-10 border-slate-300 rounded pl-1'
placeholder="cm"
inputMode="numeric"
type='text'
pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}"
value={metricMeasurement.heightInCentimetres || ''}
name="heightInCentimetres"
onChange={(e) => handleNumericInput(e, "metric")}
/>
const handleNumericInput = (e: React.ChangeEvent<HTMLInputElement>,
measurementType: "metric" | "us"
) => {
const { name, value } = e.target;
if (/^\d*\.?\d*$/.test(value)) {
if (measurementType === "metric") {
setMetricMeasurement((prev) => ({ ...prev, [name]: Number(value) }))
} else {
setUsMeasurement((prev) => ({ ...prev, [name]: Number(value)}))
}
}
};