📝

【React修行日記】useState の型付けと初期値

に公開

学習の目的

  • useState の型推論と型指定の違いを理解する
  • null や undefined を扱う場合の型付け方法を学ぶ
  • イベントオブジェクトを使うときに型を安全に指定する方法を知る
  • 簡単なカウンターアプリを型安全に実装できる

useState とは

useStateはReactで使用するフックの一つで、コンポーネントの中で値を保存して更新できる仕組み。
更新すると自動で画面が再描画される。

import { useState } from 'react';

const [hoge, setHoge] = useState(initialState)

基本的な使い方は、コンポーネントファイルにてuseStateを呼び出し、引数に初期値(initialState)を指定し、配列の形で1つ目にState変数(hoge)、2つ目にそのStateを更新するための関数(setHoge)が設定される。
State変数は自由な名前をつけることができるけど、更新関数名はset〇〇という名前にするのが一般的。

型推論と型指定

初期値を渡せば TypeScript が型を推論してくれる

const [count, setCount] = useState(0); // numberと推論される

初期値にnullを渡した場合はどうなるか?

const [name, setName] = useState(null);

setName("Alice"); // ❌ エラー
// Argument of type '"Alice"' is not assignable to parameter of type 'null'.

nullやundefinedを初期値に設定し、型を設定していないと推論がnullやundefinedのみになってしまい、例えば更新後にstringの値を渡したい場合に型エラーが出てしまう。

そのため、null から別の型に変わる」ことを許可するには union 型を明示する
例えば、string が入るけど最初はまだ値がない → null で初期化したい場合:

const [name, setName] = useState<string | null>(null);
  • 初期値 null
  • 更新後は "Alice" や "Bob" などの string
  • 型は string | null になるので両方安全に扱える

イベントハンドラと型付け

カウンターアプリの場合は、onClick に渡される関数の型を React の型定義から推論してくれるので型を明示しなくても問題ない。

const increment = () => {
  setCount((count) => count + 1);
};

イベントオブジェクトを使う場合

イベントオブジェクト(e)を使うときは型を指定した方が安全。
例えば input の値を取得する場合は React.ChangeEvent<HTMLInputElement> を使う。

const [text, setText] = useState("");

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  setText(e.target.value);
};

return (
  <div>
    <input
      type="text"
      value={text}
      onChange={handleChange}
      className="border p-2"
    />
    <p className="text-lg">入力中: {text}</p>
  </div>
);

簡易カウンターアプリ作成

useStateを利用して簡単なカウンターアプリを作成してみる...!
「+1」「-1」「リセット」ボタンのそれぞれにonClickでcountの状態を更新する関数を渡す。

import { useState } from "react";

export default function One() {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount((count) => count + 1);
  };

  const decrement = () => {
    setCount((count) => count - 1);
  };

  const reset = () => {
    setCount(0);
  };

  return (
    <div>
      <p>{count}</p>
      <div>
        <button onClick={decrement}>-1</button>
        <button onClick={increment}>+1</button>
        <button onClick={reset}>reset</button>
      </div>
    </div>
  );
}

見やすい様にスタイリングし、以下で完成!

地味に重要なポイントとしては、例えば値を増やすincrementは以下のよう更新用関数を使用して書いてる点↓

const increment = () => {
  setCount((count) => count + 1);
};

以下の様な書き方でも挙動としては同じになるが、複数回続けて書いたときに更新直前のそのStateの値が渡されないので違う挙動となる。

setCount(count + 1);

◾︎更新用関数を使用して2回続けて書いたとき

const increment = () => {
  setCount((prev) => prev + 1); //setCount(0 => 0 + 1)
  setCount((prev) => prev + 1); //setCount(1 => 1 + 1)
};

◾︎更新用関数を使用しないで2回続けて書いたとき

const increment = () => {
  setCount(count + 1); //setCount(0 + 1)
  setCount(count + 1); //setCount(0 + 1)
};

まとめ

  • useStateはコンポーネントの状態を管理するReactのフックの一つ
  • 初期値を設定することで型推論ができる
  • イベントオブジェクトを使うときは型をつける
  • 更新用関数を使用する

参考

https://ja.react.dev/reference/react/useState
https://qiita.com/k8o/items/c16a8daf03acdbd6c911

Discussion