2022-5-22
[React] input[type=number]のvalueをnumberとして扱うかstringとして扱うか
概要
Reactでtype=numberである数値入力用inputを作る際に、valueをnumberとして扱うパターンとstringとして扱うパターンがあるが、どっちの方が良いかという話。
number版
type Props = {
value: number
onChange: (value: number) => Promise<void>
}
export const NumberInput = ({value, onChange}: Props) => {
const _onChange: React.ChangeEventHandler<HTMLInputElement> = event => {
onChange(event.target.valueAsNumber)
}
return (
<input
type="number"
value={value}
onChange={onChange}
/>
)
)
string版
type Props = {
value: string
onChange: (value: string) => Promise<void>
}
export const NumberInput = ({value, onChange}: Props) => {
const _onChange: React.ChangeEventHandler<HTMLInputElement> = event => {
onChange(event.target.value)
}
return (
<input
type="number"
value={value}
onChange={onChange}
/>
)
)
結論
string型として扱うほうが事故が発生しにくい。
詳細
number型として扱う場合のメリデメは以下のようになると考えている。
- メリット
- input[type=number]の時点で、最終的にnumberとして扱いたい物であることがほとんど。なので、いちいちキャストせずnumberのまま扱えるので楽。
- デメリット
- 空入力の際の扱いをどうするのか問題
- valueAsNumberで取得するとNaNが返ってくる。NaNは当然様々な問題を引き起こす。
- Number(event.target.value)で取得するという方法もあるが、そうなると「空入力を0として扱っていいの?」という問題が出る。空入力を0としていいかエラーとすべきかは使う状況によって変わってくるのでは?
- そもそも空入力にならないよう制御するという対応
- NaNを返さないように空入力に出来ないよう制御すると使いづらい
- SmartHRの例で、空入力出来ないようにするとユーザーから問い合わせが増えたというケースがある
- 数値入力で input[type="number"]を使ったら、ユーザから問い合わせが増えてしまった話 - Qiita
- Blur時に制御する、という方式だと、onChangeのタイミングではNaNになっているので、そのタイミングで値を利用しようとされるとNaN問題が出る
- NaNを返さないように空入力に出来ないよう制御すると使いづらい
- 空入力の際の扱いをどうするのか問題
ともかく空入力の扱いがかなり難しい。ユーザーが空入力をした際に0として扱って問題ないのかもしくは何か別の対応をすべきなのかは状況によって変わるので、汎用コンポーネント側で一律制御するのは危険だと思う。
- 汎用コンポーネントはvalueをstringで扱うようにする。
- その際、汎用コンポーネントとして扱いやすいよう
Omit<React.ComponentPropsWithRef<'input'>
とforwardRef
を使用してinputタグと同様に扱えるようにしておいたほうが良い。
- その際、汎用コンポーネントとして扱いやすいよう
- 実際の具体的な状況下において、stringである入力値をどう対処するか判断
- 空入力を0として扱っても問題ないのであれば、Numberでキャストしたり、onChangeでnumberを返す制御コンポーネントを作成する等。
まとめ
valueAsNumber
の存在を最近始めて知ってめっちゃ便利じゃん!って思ったら色々考慮した結果「使い所ないなこれ…」ってなった。