投稿日:2021年5月18日
前回の記事でコンポーネント内にstateがオブジェクトの際の汎用的な更新処理をGeneric関数で作成しました。しかし、.tsx内にGeneric関数を記述する場合には問題点もあり記述が汚くなるため、できるだけ書きたくありません。この記事では更に汎用的なコンポーネントごとに使い回せるstate更新のためのファクトリーメソッドを作成してみます。
前回の記事でコンポーネント内にstateがオブジェクトの際の汎用的な更新処理をGeneric関数で作成しました。しかし、.tsx内にGeneric関数を記述する場合には問題点もあり記述が汚くなるため、できるだけ書きたくありません(泣)。
ということでこの記事では更に汎用的なコンポーネントごとに使い回せるstate更新のためのファクトリーメソッドを作成してみます。
この記事のコードは以下の環境で確認されました。
今回ファクトリーメソッドで作成する関数については前の記事のその他で作成したGeneric関数です。
すべてのコンポーネントで使える関数作成のために、それぞれのコンポーネント・関数ごとに変わる値を考えてみましょう。以下のものが考えられます。
ファクトリーメソッド作成の際には、1は型なので型引数、2と3は関数の引数で受け取れば良さそうです。自分は一つの関数の引数が多くなりすぎるのが好きでは無いので、今回は4については作成した側のコンポーネントで管理する構成にしましょう。
※もちろん4の値についてもファクトリーメソッドの引数で受け取ることもできます!
ここでは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>()がファクトリーメソッドです。型引数に渡す型と、引数から関数を生成します。
実際に前回の記事のコードを使ってファクトリーメソッドを呼び出して見ます。
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文などがすべて一つのファイルに入ってくるので、これくらい完結にかけるほうが良い気がしますね。