React useStateを使って状態管理を行う
										はい、hebiです。
今回はコンポーネント内でデータの状態を管理する方法を記事にしたいと思います。
画面遷移時に受け取ったデータ使って状態管理する記事にしているので、まだの方は以下の記事ををどうぞ!

状態管理とは

状態管理ってなんなの?

ユーザーの操作、外部からのデータの受信、その他のイベントによって変化するデータを管理することを状態管理というのじゃよ。

あ~じゃあ前回の画面遷移で受け取ったデータも管理させることができるということか。

そうじゃ。例えば受け取ったデータをテキストボックスで表示し、変更があればその状態を管理することができるのじゃ。
今回は状態管理を行うためのuseStateについて説明していくとしよう!
useStatuについて
ReactではuseStateを利用して状態を管理します。
useStateを使うことでコンポーネントが内部で保持する「状態」を管理することができます。
例えばテキストボックスに入力した値やチェックボックスのON/OFFの状態を管理します。
さっそくテキストボックスとチェックボックスの値をuseStateを使って状態を管理してみましょう。
【useState】テキストボックス編
まずはテキストボックスで試してみましょう!
useStateの定義
useStateを利用できるように定義します。
import React, { useState } from "react";useStateフックを呼び出す
useStateを呼び出し、状態変数(name)とその更新関数(setName)を宣言します。
const [name, setName] = useState(location.state.text);- useStateの引数には初期値を設定します。
画面遷移で受けっとたパラメータを設定すると、const [name, setName] のnameにlocation.state.textの値が入ります。 - string、number、booleanなど暗黙の型とよばれるデータはuseStateを利用する際に型指定は不要です。(location.state.textはstringのため型指定していないです。)
 
状態の確認
この状態でnameにちゃんと値が入っているかを確認してみましょう。
import React, { useState } from "react";
import CustomButton from "./CustomButton";
import { useLocation } from "react-router-dom";
const EditPage = () => {
  const location = useLocation();
  const [name, setName] = useState(location.state.text);
  const onClick = () => {
    alert("保存しました");
  };
  return (
    <div>
      <div>編集画面</div>
      <input type="text" value={name} />
      <CustomButton width="100px" onClick={onClick}>
        保存
      </CustomButton>
    </div>
  );
};
export default EditPage;

おっ、valueにnameを指定したらちゃんと表示されたぞ!useStateに指定したlocation.state.textの値が表示されたんだな!


あれ、テキストボックスに入力できないぞ・・・

今のままだとテキストボックスの値を状態管理していないから入力しても変わらないのじゃ。次に状態の変更に対応していくぞ。
状態の変更
今のままだとテキストボックスに値を入力できない状態です。ここが通常のHTMLで実装した場合と異なります。
inputのChangeイベントで取得した入力値を更新関数(setName)にセットしましょう。
const onTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  setName(e.target.value);
};
おー!入力できるようになったぞ!
setNameに指定した値がnameに入るんだな!


useStateの使い方は理解できたかの?
おさらいとしてチェックボックスで試してみるのじゃ
【useState】チェックボックス編
次にチェックボックスで試してみましょう!
useStateフックを呼び出す
useStateを呼び出し、状態変数(permission)とその更新関数(setPermission)を宣言します。初期値はtrueとしました。
const [permission, setPermission] = useState(true);状態の確認
この状態でpermissionにちゃんと値が入っているかを確認してみましょう。
import React, { useState } from "react";
import CustomButton from "./CustomButton";
import { useLocation } from "react-router-dom";
const EditPage = () => {
  const location = useLocation();
  const [name, setName] = useState(location.state.text);
  const [permission, setPermission] = useState(true);
  const onClick = () => {
    alert("保存しました");
  };
  const onTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setName(e.target.value);
  };
  return (
    <div>
      <div>編集画面</div>
      <input type="text" value={name} onChange={onTextChange} />
      <input type="checkbox" id="permission" checked={permission} />
      <CustomButton width="100px" onClick={onClick}>
        保存
      </CustomButton>
    </div>
  );
};
export default EditPage;

チェックボックスがON状態で表示されましたね!
今の状態だとOFFにできないので、状態の変更に対応していきます。
状態の変更
inputのChangeイベントで取得した入力値を更新関数(setPermission)にセットしましょう。
  const onCheckedChange = () => {
    setPermission(!permission);
  };ソースコードのまとめ

上記まとめたソースコードじゃ。これでuseStateはばっちりじゃな。
import React, { useState } from "react";
import CustomButton from "./CustomButton";
import { useLocation } from "react-router-dom";
const EditPage = () => {
  const location = useLocation();
  const [name, setName] = useState(location.state.text);
  const [permission, setPermission] = useState(true);
  const onClick = () => {
    alert("保存しました");
  };
  const onTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setName(e.target.value);
  };
  const onCheckedChange = () => {
    setPermission(!permission);
  };
  return (
    <div>
      <div>編集画面</div>
      <div>
        <label htmlFor="name">名前</label>
        <input type="text" id="name" onChange={onTextChange} />
      </div>
      <div>
        <label htmlFor="permission">許諾</label>
        <input
          type="checkbox"
          id="permission"
          checked={permission}
          onChange={onCheckedChange}
        />
      </div>
      <CustomButton width="100px" onClick={onClick}>
        保存
      </CustomButton>
    </div>
  );
};
export default EditPage;

もしinputタグが5個あったら全部にChangeイベントを実装しないといけないの??大変だなぁ。

そのような疑問を持つことはとても大切じゃぞ。
1つにまとめることも可能じゃ。
状態を1つにまとめる
inputタグ全てに対してChangeイベントを実装するのは大変ですよね。
そこで、1つにまとめることも可能です!
まとめて状態を管理してみましょう。
まずは型の定義とstateを定義します。
型の定義
状態管理したい方の定義を行います。今回は3つ定義してみました。
interface TestForm {
    firstName: string;
    age: number;
    permission: boolean;
}useStateフックを呼び出す
useStateを呼び出し、状態変数(testForm)とその更新関数(setTestForm)を宣言します。初期値はTestForm型の空({})としました。
const [testForm, setTestForm] = useState<TestForm>({} as TestForm);inputタグとプロパティの紐づけ
識別できるようにinputタグのnameにそれぞれのプロパティ名を設定します。
- 名前のinputタグ→farstName
 - 年齢のinputタグ→age
 - 許諾のinputタグ→permission
 
  return (
    <div>
      <div>ログイン画面</div>
      <div>
        <label htmlFor="firstName">名前</label>
        <input
          type="text"
          name="firstName"
          id="firstName"
          value={testForm.firstName}
          onChange={onChange}
        />
      </div>
      <div>
        <label htmlFor="age">年齢</label>
        <input
          type="number"
          name="age"
          id="age"
          value={testForm.age}
          onChange={onChange}
        />
      </div>
      <div>
        <label htmlFor="permission">許諾</label>
        <input
          type="checkbox"
          id="permission"
          name="permission"
          checked={testForm.permission}
          onChange={onChange}
        />
      </div>
      <CustomButton width="100px" onClick={onClick}>
        ログイン
      </CustomButton>
    </div>
  );状態の変更
Changeイベントにてnameに紐づく値を設定します。
checkboxはvalueにbooleanを持たせることがでず、checkedにONOFF値を持たせているためtypeでcheckedかvalueを判断するようにしています。
  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value, checked, type } = e.target;
    setTestForm((formData) => ({
      ...formData,
      [name]: type === "checkbox" ? checked : value,
    }));
  };まとめ
まとめると以下のようになります。ログイン画面コンポーネントとして作成してみました。
import React, { useState } from "react";
import CustomButton from "./CustomButton";
const LoginPage = () => {
  interface TestForm {
    firstName: string;
    age: number;
    permission: boolean;
  }
  const [testForm, setTestForm] = useState<TestForm>({} as TestForm);
  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value, checked, type } = e.target;
    setTestForm((formData) => ({
      ...formData,
      [name]: type === "checkbox" ? checked : value,
    }));
  };
  const onClick = () => {
    alert(testForm.firstName + "," + testForm.age + "," + testForm.permission);
  };
  return (
    <div>
      <div>ログイン画面</div>
      <div>
        <label htmlFor="firstName">名前</label>
        <input
          type="text"
          name="firstName"
          id="firstName"
          value={testForm.firstName}
          onChange={onChange}
        />
      </div>
      <div>
        <label htmlFor="age">年齢</label>
        <input
          type="number"
          name="age"
          id="age"
          value={testForm.age}
          onChange={onChange}
        />
      </div>
      <div>
        <label htmlFor="permission">許諾</label>
        <input
          type="checkbox"
          id="permission"
          name="permission"
          checked={testForm.permission}
          onChange={onChange}
        />
      </div>
      <CustomButton width="100px" onClick={onClick}>
        ログイン
      </CustomButton>
    </div>
  );
};
export default LoginPage;

ちょっと難しかったかの。
これも慣れじゃから少しずつ状態管理を行っていくといいぞ。

ありがとう!がんばってみるよ
最後に
useStateの使い方いかがだったでしょうか?
名前、年齢、メールアドレス、パスワードなどログインで必要な情報をstate管理してみて理解していただけたらと思います!!
次はBootstrapを使ったレスポンシブな画面を作成していきましょう。

最後までお読みいただきありがとうございました。
