
目次
ReactのuseStateでフォームの数字の扱いの間違い
ReactのuseStateでフォームの数字を変数に追加したいケースがよくあります。
しかし、Reactおよびフォームの仕様を理解していないと、意図しない結果になってしまうことがあります。
例えば、以下は <input type="number"> に数字を入力して計算結果を画面上に表示する処理なのですが、間違っている箇所が3つあります。
※ const [num, setNum] = useState<number | null>(null) の部分は正しいものとする
import { useState } from 'react'
import './App.css'
function App() {
const [num, setNum] = useState<number | null>(null)
return (
<>
<h1>React計算機</h1>
<input
type="number"
value={num}
onChange={(e) => {
setNum(e.target.value)
}}
/>
<p>1 + {num} = {num ? 1 + num : ''}</p>
</>
)
}
export default App
こちらのコードを見て、どこが間違っているかわからなければ、Reactでコードを書く際には注意が必要です。
以降でどこが間違っているのか説明します。
間違い1: valueにnullが入っている
value={num} だと初期値のnullが入りますが、valueにnullを追加してはいけません。
ブラウザのConsoleを確認すると、valueに「空文字」を入れるようにとエラーが表示されてしまいます。

valueがnullでも画面上の <input type="number"> には何も表示されないので、間違っていても気づかないことがよくあります。
nullのときは空文字を返すように value={num ?? ''} に変更してください。
import { useState } from 'react'
import './App.css'
function App() {
const [num, setNum] = useState<number | null>(null)
return (
<>
<h1>React計算機</h1>
<input
type="number"
value={num ?? ''}
onChange={(e) => {
setNum(e.target.value)
}}
/>
<p>1 + {num} = {num ? 1 + num : ''}</p>
</>
)
}
export default App
ちなみに value={num || ''} だと 0 も空文字になってしまうので、このケースでは使用してはいけません。
間違い2: useState<number | null>でstringを代入
useState<number | null>なのにsetNum(e.target.value)で文字列を入れています。
フォームで入力された値は <input type="number"> だとしても、e.target.valueで取得するのは文字列の数字なので以下のようにNumberで数値に変換してからsetNumにセットする必要があります。
ただし、Number(e.target.value) にすると、空文字のときに Number('') が0になるため、問題があります。
空文字のときはnullにする必要があるので、setNum(value === '' ? null : Number(value)) にして追加します。
import { useState } from 'react'
import './App.css'
function App() {
const [num, setNum] = useState<number | null>(null)
return (
<>
<h1>React計算機</h1>
<input
type="number"
value={num ?? ''}
onChange={(e) => {
const value = e.target.value
setNum(value === '' ? null : Number(value))
}}
/>
<p>1 + {num} = {num ? 1 + num : ''}</p>
</>
)
}
export default App
間違い3: num ? 1 + num : '' で0ときの考慮漏れ
「num ? 1 + num : ''」だと num が「0」のときも空文字が返されてしまいます。
この場合は「num !== null ? 1 + num : ''」で判定することで正しい結果を返せます。
import { useState } from 'react'
import './App.css'
function App() {
const [num, setNum] = useState<number | null>(null)
return (
<>
<h1>React計算機</h1>
<input
type="number"
value={num ?? ''}
onChange={(e) => {
const value = e.target.value
setNum(value === '' ? null : Number(value))
}}
/>
<p>1 + {num} = {num !== null ? 1 + num : ''}</p>
</>
)
}
export default App
ESLintでnullの考慮漏れを検出する
前述の「num ? 1 + num : ''」のような間違いはESLintのstrict-boolean-expressionsのルールを追加することで検出できます。
ESLintの推奨設定(eslint:recommended)にはstrict-boolean-expressionsは含まれていないため、必ずルールを追加することを推奨します。
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist', 'vite.config.ts'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parser: tseslint.parser,
parserOptions: {
project: './tsconfig.app.json',
tsconfigRootDir: '.',
},
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'@typescript-eslint/strict-boolean-expressions': 'error',
},
}
)
strict-boolean-expressions | typescript-eslint