プログラミング

React useStateを使って状態管理を行う

hebishima.shogo

はい、hebiです。

今回はコンポーネント内でデータの状態を管理する方法を記事にしたいと思います。

画面遷移時に受け取ったデータ使って状態管理する記事にしているので、まだの方は以下の記事ををどうぞ!

あわせて読みたい
Reactで画面遷移を行う
Reactで画面遷移を行う
スポンサーリンク

状態管理とは

ぺんぎん
ぺんぎん

状態管理ってなんなの?

hebiじぃ
hebiじぃ

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

ぺんぎん
ぺんぎん

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

hebiじぃ
hebiじぃ

そうじゃ。例えば受け取ったデータをテキストボックスで表示し、変更があればその状態を管理することができるのじゃ。
今回は状態管理を行うためのuseStateについて説明していくとしよう!

useStatuについて

ReactではuseStateを利用して状態を管理します。
useStateを使うことでコンポーネントが内部で保持する「状態」を管理することができます。

例えばテキストボックスに入力した値やチェックボックスのON/OFFの状態を管理します。

さっそくテキストボックスとチェックボックスの値をuseStateを使って状態を管理してみましょう。

useStateはpropsとは違い、後から変更することができます。

【useState】テキストボックス編

まずはテキストボックスで試してみましょう!

useStateの定義

useStateを利用できるように定義します。

import React, { useState } from "react";

useStateフックを呼び出す

useStateを呼び出し、状態変数(name)とその更新関数(setName)を宣言します。

const [name, setName] = useState(location.state.text);
ポイント
  1. useStateの引数には初期値を設定します。
    画面遷移で受けっとたパラメータを設定すると、const [name, setName] のnameにlocation.state.textの値が入ります。
  2. string、number、booleanなど暗黙の型とよばれるデータはuseStateを利用する際に型指定は不要です。(location.state.textはstringのため型指定していないです。)

暗黙の型以外を状態管理する場合は型を指定する必要があります。

  interface TestForm {
    name: string;
    age: number;
    permission: boolean;
  }

  const [testForm, setTestForm] = useState<TestForm>({} as TestForm);

状態の確認

この状態で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の値が表示されたんだな!

ぺんぎん
ぺんぎん

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

hebiじぃ
hebiじぃ

今のままだとテキストボックスの値を状態管理していないから入力しても変わらないのじゃ。次に状態の変更に対応していくぞ。

状態の変更

今のままだとテキストボックスに値を入力できない状態です。ここが通常のHTMLで実装した場合と異なります。

inputのChangeイベントで取得した入力値を更新関数(setName)にセットしましょう。

const onTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  setName(e.target.value);
};
ぺんぎん
ぺんぎん

おー!入力できるようになったぞ!
setNameに指定した値がnameに入るんだな!

hebiじぃ
hebiじぃ

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);
  };

チェックボックスはONまたはOFFのみのため、!permissionを渡すことで現在と逆の状態で更新するように実装してます。

ソースコードのまとめ

hebiじぃ
hebiじぃ

上記まとめたソースコードじゃ。これで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イベントを実装しないといけないの??大変だなぁ。

hebiじぃ
hebiじぃ

そのような疑問を持つことはとても大切じゃぞ。
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,
    }));
  };

formDataのように複数のパラメータを持つ場合、以下のように設定するだけではstateは更新されません。…(スプレッド演算子)でコピーした値を設定する必要があります。

setTestForm(testForm)

まとめ

まとめると以下のようになります。ログイン画面コンポーネントとして作成してみました。

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;
hebiじぃ
hebiじぃ

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

ぺんぎん
ぺんぎん

ありがとう!がんばってみるよ

最後に

useStateの使い方いかがだったでしょうか?
名前、年齢、メールアドレス、パスワードなどログインで必要な情報をstate管理してみて理解していただけたらと思います!!

次はBootstrapを使ったレスポンシブな画面を作成していきましょう。

あわせて読みたい
ReactとBootstrapでレスポンシブな画面作成
ReactとBootstrapでレスポンシブな画面作成

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

スポンサーリンク
ABOUT ME
hebi
hebi
エンジニア
フルスタックエンジニアとして活躍中。
HTML5プロフェッショナル認定Level1、Level2所持者です。

未経験の方でも簡単にプログラミングを学べるようにと情報を発信しております。
記事URLをコピーしました