オブジェクトの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 だけあとから上書きされます。namebio は元のまま引き継がれます。

順番が大事です。次のように書くと、展開があとに来るので上書きが打ち消されてしまいます。

setProfile({ title: "フロントエンドエンジニア", ...profile });

変えたいキーは必ずスプレッドのうしろ と覚えてください。

スプレッドを忘れるとどうなるか

setProfile({ title: "フロントエンドエンジニア" });

これは title しか持たない別のオブジェクトになります。画面では profile.nameundefined になり、名前が消えます。「肩書きを変えたら名前が消えた」という不具合の原因はたいていこれです。

前の値から作る形

こちらも更新関数に関数を渡す書き方が使えます。

setProfile((prev) => ({ ...prev, title: "フロントエンドエンジニア" }));

アロー関数でオブジェクトをそのまま返すときは、丸かっこで包む必要があります。包まないと波かっこが関数の本体だと解釈され、何も返らなくなります。

演習

プロフィールをオブジェクトの state で持ち、肩書きと自己紹介をボタンで書きかえます。

ヒント

App.jsx
App.jsx
プレビュー

できているか

  • はじめの肩書きは Web エンジニア になっている
  • 肩書きを変えるボタンを押すと title だけが変わる
  • 両方のボタンを押すと肩書きと自己紹介がどちらも新しくなる