※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。

次のページ>
【React + TypeScript】stateの更新をGenericsとファクトリーメソッドで実装する

【React + TypeScript】オブジェクトstateの管理方法

react web開発 フロント開発 TypeScript 型推論 型引数

投稿日:2021年5月14日

このエントリーをはてなブックマークに追加
この記事ではuseStateで更新するstateの対象でオブジェクトをする場合の基本的な管理方法や、個人的なおすすめ実装方法について解説しています。

はじめに

この記事について

この記事ではuseStateで更新するstateの対象でオブジェクトをする場合の基本的な管理方法や、個人的なおすすめ実装方法について解説しています。

環境について

この記事は以下の環境で確認する

  • TypeScript version 4.2.3
  • react version 17.0.2

実践

簡単な実装

まずは以下のような簡単な型を持ったstateを持つコンポーネントについて考えてみます。

interface SimpleObject {
  firstName: string;
  lastName: string;
  age: number;
}

この様な複数のキーのObjectのstateは以下の様にスプレッド構文(spread operator)を使って展開すると楽です。

// stateを作成
  const [simpleObject, setSimpleObject] = useState<SimpleObject>({
    firstName: "",
    lastName: "",
    age: 0,
  });

  // firstNameを変更する関数
  const changeFirstName = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSimpleObject({ ...simpleObject, firstName: e.target.value });
  };

実際のサンプルコードは以下の様になります。

import React, { useState } from "react";

interface SimpleObject {
  firstName: string;
  lastName: string;
  age: number;
}

const SimpleObjectComponet = () => {
  // stateを作成
  const [simpleObject, setSimpleObject] = useState<SimpleObject>({
    firstName: "",
    lastName: "",
    age: 0,
  });

  // firstNameを変更する関数
  const changeFirstName = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSimpleObject({ ...simpleObject, firstName: e.target.value });
  };

  // lastNameを変更する関数
  const changeLastName = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSimpleObject({ ...simpleObject, lastName: e.target.value });
  };

  // ageを変更する関数
  const changeAge = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSimpleObject({ ...simpleObject, age: Number(e.target.value) });
  };
  return (
    <div>
      <p>
        <input
          type="text"
          value={simpleObject.firstName}
          onChange={changeFirstName}
        ></input>
        {simpleObject.firstName}
      </p>
      <p>
        <input
          type="text"
          value={simpleObject.lastName}
          onChange={changeLastName}
        ></input>
        {simpleObject.lastName}
      </p>
      <p>
        <input
          value={simpleObject.age}
          type="number"
          onChange={changeAge}
        ></input>
        {simpleObject.age}
      </p>
    </div>
  );
};

export default SimpleObjectComponet;

上記コードはchangeFirstName()changeLastName()changeAge()のすべてがsetSimpleObject()渡すオブジェクトを...simpleObjectを展開した元データ部分と、keyvalueを直接指定するデータ更新部分に別れています。もちろん一つ一つ渡すこともできますが、後々のメンテナンスを考えるとあまり現実的ではありませんね。

【おまけ】おすすめ実装方法

オブジェクトに値をセットするGeneric関数を作っておくと、バリデーションなどがしやすくて便利かもしれませんね。

Generic関数で共通化したコンポーネント
import React, { useState } from "react";

interface SimpleObject {
  firstName: string;
  lastName: string;
  age: number;
}

const SimpleObjectComponet = () => {
  // stateを作成
  const [simpleObject, setSimpleObject] = useState<SimpleObject>({
    firstName: "",
    lastName: "",
    age: 0,
  });

  /**
   * SimpleObjectを更新する汎用関数
   * @param key
   * @param value
   */
  const updateSimpleObject = <
    K extends keyof SimpleObject,
    V extends SimpleObject[K]
  >(
    key: K,
    value: V
  ) => {
    setSimpleObject({ ...simpleObject, [key]: value });
    // バリデーションを入れる場合ここ
  };

  /**
   * firstNameを更新
   * @param e
   */
  const changeFirstName = (e: React.ChangeEvent<HTMLInputElement>) => {
    updateSimpleObject("firstName", e.target.value);
  };

  // lastNameを変更する関数
  const changeLastName = (e: React.ChangeEvent<HTMLInputElement>) => {
    updateSimpleObject("lastName", e.target.value);
  };

  // ageを変更する関数
  const changeAge = (e: React.ChangeEvent<HTMLInputElement>) => {
    updateSimpleObject("age", Number(e.target.value));
  };

  return (
    <div>
      <p>
        <input
          type="text"
          value={simpleObject.firstName}
          onChange={changeFirstName}
        ></input>
        {simpleObject.firstName}
      </p>
      <p>
        <input
          type="text"
          value={simpleObject.lastName}
          onChange={changeLastName}
        ></input>
        {simpleObject.lastName}
      </p>
      <p>
        <input
          value={simpleObject.age}
          type="number"
          onChange={changeAge}
        ></input>
        {simpleObject.age}
      </p>
    </div>
  );
};

export default SimpleObjectComponet;

上記コードはupdateSimpleObject<>()SimpleObjectの更新処理をまとめました。ここで型引数について注目してみましょう。Generic関数は基本的には型引数に特定の型を指定して関数の動作を決めます。しかし、今回の場合は関数の引数で型引数をそのまま渡しています。そのような場合、関数の引数から型引数が推測され型チェックが行われるため、汎用性の高い引数制限を作ることができます。

このエントリーをはてなブックマークに追加

次のページ>
【React + TypeScript】stateの更新をGenericsとファクトリーメソッドで実装する

関連記事

記事へのコメント