オブジェクトのstate
まとまった値を 1 つの state にする
プロフィールの名前と肩書きと自己紹介は、いつも一緒に扱う値です。こういうものは 1 つのオブジェクトを state にすると見通しがよくなります。
const [profile, setProfile] = useState({
name: "あおい",
title: "Web エンジニア",
bio: "動くものを作るのが好きです。JavaScript と React を勉強中です。",
});表示するときは profile.name のようにキーで取り出します。
<h1>{profile.name}</h1>
<p>{profile.title}</p>直接書きかえてはいけない
肩書きだけ変えたいとき、つい次のように書きたくなります。
function promote() {
profile.title = "フロントエンドエンジニア";
}これは動きません。オブジェクトの中身は確かに変わりますが、profile という 入れ物そのものは同じまま です。React は「前と違うものが来たか」を入れ物どうしの比較で判断するので、中身だけ変えても同じものだと見なし、画面を描き直しません。第 1 章で見た「変数を書きかえても画面が変わらない」のと同じことが、オブジェクトの中でも起きます。
新しいオブジェクトを作って渡す
正しいやり方は、元のオブジェクトをコピーして、変えたいキーだけ上書きした新しいオブジェクト を作ることです。ここでスプレッド構文が効きます。
function promote() {
setProfile({ ...profile, title: "フロントエンドエンジニア" });
}...profile が「今の profile の全キーをここに展開する」という意味です。そのうしろに title を書いているので、title だけあとから上書きされます。name と bio は元のまま引き継がれます。
順番が大事です。次のように書くと、展開があとに来るので上書きが打ち消されてしまいます。
setProfile({ title: "フロントエンドエンジニア", ...profile });変えたいキーは必ずスプレッドのうしろ と覚えてください。
スプレッドを忘れるとどうなるか
setProfile({ title: "フロントエンドエンジニア" });これは title しか持たない別のオブジェクトになります。画面では profile.name が undefined になり、名前が消えます。「肩書きを変えたら名前が消えた」という不具合の原因はたいていこれです。
前の値から作る形
こちらも更新関数に関数を渡す書き方が使えます。
setProfile((prev) => ({ ...prev, title: "フロントエンドエンジニア" }));アロー関数でオブジェクトをそのまま返すときは、丸かっこで包む必要があります。包まないと波かっこが関数の本体だと解釈され、何も返らなくなります。
演習
プロフィールをオブジェクトの state で持ち、肩書きと自己紹介をボタンで書きかえます。