React TypeScript:useState・useRef・Props の型指定まとめ

スポンサーリンク

React TypeScript:useState・useRef・Props の型指定まとめ

React と TypeScript を組み合わせるときに迷いやすい型指定のパターンをまとめます。


useState の型指定

初期値から型が推論される

単純な値は推論に任せて書けます。

const [count, setCount] = useState(0)       // number
const [name, setName] = useState("")        // string
const [flag, setFlag] = useState(false)     // boolean

null を含む場合は明示が必要

// NG:user の型が null に固定される
const [user, setUser] = useState(null)

// OK:User | null を明示する
type User = { id: number; name: string }
const [user, setUser] = useState<User | null>(null)

空配列は型が never[] になる

// NG:items の型が never[] になる
const [items, setItems] = useState([])

// OK:型引数を指定する
const [items, setItems] = useState<string[]>([])
const [users, setUsers] = useState<User[]>([])

setState を Props に渡す

setState を子コンポーネントに渡すときは Dispatch<SetStateAction<T>> を使います。

import { Dispatch, SetStateAction } from "react"

type Props = {
  setCount: Dispatch<SetStateAction<number>>
}

function Child({ setCount }: Props) {
  return <button onClick={() => setCount((n) => n + 1)}>+1</button>
}

useRef の型指定

DOM 要素を参照する

DOM を参照する場合は要素の型を指定します。初期値は null を渡します。

// input 要素
const inputRef = useRef<HTMLInputElement>(null)

// div 要素
const divRef = useRef<HTMLDivElement>(null)

// button 要素
const btnRef = useRef<HTMLButtonElement>(null)

ref.current は null チェックが必要

DOM がマウントされる前は currentnull のため、オプショナルチェーン(?.)を使います。

const inputRef = useRef<HTMLInputElement>(null)

const handleFocus = () => {
  inputRef.current?.focus()        // null の場合は何もしない
  inputRef.current?.select()
}

return <input ref={inputRef} />

ミュータブルな値を保持する(再レンダリングしない)

タイマー ID などレンダリングに影響しない値を保持する場合は初期値に値を渡します。このとき currentnull になりません。

// タイマー ID の保持
const timerRef = useRef<number>(0)

const start = () => {
  timerRef.current = window.setInterval(() => {
    // ...
  }, 1000)
}

const stop = () => {
  clearInterval(timerRef.current)
}

DOM 参照(初期値 null)とミュータブル値(初期値あり)で current の型が変わります。


Props の型指定

children

import { ReactNode } from "react"

type Props = {
  children: ReactNode          // テキスト・要素・配列など何でも受け取れる
}

// JSX.Element だと文字列などを渡せないため ReactNode が汎用的

イベントハンドラ

type Props = {
  onClick: () => void
  onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
  onSubmit: (e: React.FormEvent<HTMLFormElement>) => void
  onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void
}

style

インラインスタイルを Props で渡す場合は React.CSSProperties を使います。

type Props = {
  style?: React.CSSProperties
}

// 使う側
<MyComponent style={{ color: "red", fontSize: 14 }} />

オプショナルな Props

? を付けるとオプショナルになります。

type Props = {
  label: string          // 必須
  placeholder?: string   // 省略可能
  disabled?: boolean     // 省略可能(デフォルト undefined)
}

よく使う型まとめ

用途
useState の null 許容 useState<T | null>(null)
setState を Props に渡す Dispatch<SetStateAction<T>>
input の DOM 参照 useRef<HTMLInputElement>(null)
div の DOM 参照 useRef<HTMLDivElement>(null)
children ReactNode
input の onChange React.ChangeEvent<HTMLInputElement>
インラインスタイル React.CSSProperties
クリックイベント React.MouseEvent<HTMLButtonElement>

TypeScript のジェネリクス全般については「TypeScript ジェネリクス入門:<T> の基本と型制約の書き方」も参照してください。