投稿日:2021年5月14日
この記事ではuseStateで更新するstateの対象でオブジェクトをする場合の基本的な管理方法や、個人的なおすすめ実装方法について解説しています。
この記事ではuseStateで更新するstateの対象でオブジェクトをする場合の基本的な管理方法や、個人的なおすすめ実装方法について解説しています。
この記事は以下の環境で確認する
まずは以下のような簡単な型を持った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を展開した元データ部分と、keyとvalueを直接指定するデータ更新部分に別れています。もちろん一つ一つ渡すこともできますが、後々のメンテナンスを考えるとあまり現実的ではありませんね。
オブジェクトに値をセットする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関数は基本的には型引数に特定の型を指定して関数の動作を決めます。しかし、今回の場合は関数の引数で型引数をそのまま渡しています。そのような場合、関数の引数から型引数が推測され型チェックが行われるため、汎用性の高い引数制限を作ることができます。