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

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

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

react web開発 フロント開発 TypeScript ファクトリーメソッド Ge

投稿日:2021年5月18日

このエントリーをはてなブックマークに追加
前回の記事でコンポーネント内にstateがオブジェクトの際の汎用的な更新処理をGeneric関数で作成しました。しかし、.tsx内にGeneric関数を記述する場合には問題点もあり記述が汚くなるため、できるだけ書きたくありません。この記事では更に汎用的なコンポーネントごとに使い回せるstate更新のためのファクトリーメソッドを作成してみます。

はじめに

この記事について

前回の記事でコンポーネント内にstateがオブジェクトの際の汎用的な更新処理をGeneric関数で作成しました。しかし、.tsx内にGeneric関数を記述する場合には問題点もあり記述が汚くなるため、できるだけ書きたくありません(泣)。

ということでこの記事では更に汎用的なコンポーネントごとに使い回せるstate更新のためのファクトリーメソッドを作成してみます。

環境

この記事のコードは以下の環境で確認されました。

  • typescript version 4.2.3
  • react version 17.0.2
  • react-scripts version 4.0.3

実践

汎用性のために

今回ファクトリーメソッドで作成する関数については前の記事のその他で作成したGeneric関数です。

すべてのコンポーネントで使える関数作成のために、それぞれのコンポーネント・関数ごとに変わる値を考えてみましょう。以下のものが考えられます。

  1. useState<T>()で型引数Tに渡すstateの型
  2. useState<T>()で返される配列の1つ目、stateオブジェクト
  3. useState<T>()で返される配列の2つ目、state更新関数
  4. stateオブジェクトの更新対象のkey、value

ファクトリーメソッド作成の際には、1は型なので型引数、23は関数の引数で受け取れば良さそうです。自分は一つの関数の引数が多くなりすぎるのが好きでは無いので、今回は4については作成した側のコンポーネントで管理する構成にしましょう。

※もちろん4の値についてもファクトリーメソッドの引数で受け取ることもできます!

ファクトリーメソッドの作成

ここではutils.tsというファイルを作成して、その中にファクトリーメソッドを作成します。

utils.ts
import React from "react";

/**
 * Reactの関数コンポーネントのstateを更新する関数を作成する
 */
export class UpdateStateFactory {
  /**
   * stateのオブジェクトの更新処理をする関数を取得
   * @param updateState useStateで生成される第2引数。
   * @param stateObj 更新対象のオブジェクト。useStateで生成される。
   * @returns
   */
  static getUpdateMethod<T>(
    stateObj: T,
    updateState: React.Dispatch<React.SetStateAction<T>>
  ): <K extends keyof T, V extends T[K]>(key: K, value: V) => void {
    return <K extends keyof T, V extends T[K]>(key: K, value: V) => {
      updateState({ ...stateObj, [key]: value });
    };
  }
}

今回のファクトリーメソッドは特に設定の管理などは必要ないためstatic関数にしました。getUpdateMethod<T>()がファクトリーメソッドです。型引数に渡す型と、引数から関数を生成します。

ファクトリーメソッドをコンポーネントから呼び出し

実際に前回の記事のコードを使ってファクトリーメソッドを呼び出して見ます。

SimpleObjectComponent.tsx
import React, { useState } from "react";
import { UpdateStateFactory } from "./utils";

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 = UpdateStateFactory.getUpdateMethod<SimpleObject>(
    simpleObject,
    setSimpleObject
  );

  /**
   * 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;

TypeScriptで書いたコンポーネントは型やJSX文などがすべて一つのファイルに入ってくるので、これくらい完結にかけるほうが良い気がしますね。

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

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

関連記事

記事へのコメント