分割代入とスプレッド構文

Part 0 · React の前に

分割代入とスプレッド構文

props の正体を読み解くための前提知識

React のコードには、見慣れない記号が 2 つよく出てきます。

const { name } = props の波括弧と、{ ...user, age: 21 } の 3 つの点です。

どちらも JavaScript の書き方で、React の機能ではありません。ただ、この 2 つを知らないと props も state の更新も読めません。 先に片付けておきます。

分割代入 — 必要なものだけ取り出す

オブジェクトから値を取り出すとき、ふつうはこう書きます。

const name = user.name;
const age = user.age;

これを 1 行で書けるようにしたのが分割代入です。

const { name, age } = user;

波括弧の中に書いた名前と同じ項目を、オブジェクトから探して取り出します。 順番は関係ありません。名前で見ています。

オブジェクトと配列から取り出す

オブジェクトは名前で、配列は順番で取り出す

オブジェクトから
name は "さとう"、age は 20
配列から
1 つめは "赤"、2 つめは "青"

配列の場合は角括弧を使い、書いた順番で取り出されます。 配列の要素には名前がないためです。

destructuring.ts

const user = { name: "さとう", age: 20, city: "東京" };

// 必要なものだけ、名前を指定して取り出す
const { name, age } = user;

export const picked = `name は "${name}"、age は ${age}`;

// 配列は名前ではなく「順番」で取り出す
const colors = ["赤", "青", "緑"];
const [first, second] = colors;

export const firstTwo = `1 つめは "${first}"、2 つめは "${second}"`;

props でよく見る形

分割代入は、関数の引数のところにも書けます。 React のコンポーネントはこの形をしていることがほとんどです。

// 受け取ってから取り出す
function Hello(props) {
  const { name } = props;
  return <p>{name} さん</p>;
}

// 受け取るところで直接取り出す(こちらが主流)
function Hello({ name }) {
  return <p>{name} さん</p>;
}

下の { name } は、オブジェクトを受け取ってその場で中身を取り出しています。引数が 1 つのオブジェクトであるという点は変わりません。

destructuring.ts

const user = { name: "さとう", age: 20, city: "東京" };

// 必要なものだけ、名前を指定して取り出す
const { name, age } = user;

export const picked = `name は "${name}"、age は ${age}`;

// 配列は名前ではなく「順番」で取り出す
const colors = ["赤", "青", "緑"];
const [first, second] = colors;

export const firstTwo = `1 つめは "${first}"、2 つめは "${second}"`;

スプレッド構文 — 中身を展開する

... は「中身をその場に展開する」記号です。 新しいオブジェクトを作るときに使います。

const original = { name: "さとう", age: 20 };

const copied = { ...original };
// { name: "さとう", age: 20 } と書いたのと同じ

const updated = { ...original, age: 21 };
// 展開したあとに書いたものが勝つ

{ ...original, age: 21 } は 「original の中身を全部並べて、そのあと age だけ書き直す」という意味です。 あとに書いたほうが勝つので、age が上書きされます。

コピーして一部だけ変える

新しいほうだけが変わり、元は残っている

updated(新しく作ったもの)
{ name: "さとう", age: 21 }
original(元のまま)
{ name: "さとう", age: 20 }

大事なのは、元のオブジェクトが変わっていないことです。 スプレッド構文は書き換えではなく、新しいものを作るための道具です。

spread.ts

const original = { name: "さとう", age: 20 };

// ... で中身を展開して、新しいオブジェクトを作る
export const copied = { ...original };

// 展開したあとに書けば、その項目だけ上書きできる
export const updated = { ...original, age: 21 };

// original はいっさい変わっていない
export const stillOriginal = original;

配列でも同じことができる

const members = ["さとう", "すずき"];

// 末尾に足した新しい配列
const added = [...members, "たかはし"];

// 先頭に足した新しい配列
const addedToTop = ["やまだ", ...members];

push は元の配列を書き換えてしまいますが、 この書き方なら元は残ったまま、新しい配列ができます。 React で配列の state を更新するときは、いつもこちらを使います。

spread.ts

const original = { name: "さとう", age: 20 };

// ... で中身を展開して、新しいオブジェクトを作る
export const copied = { ...original };

// 展開したあとに書けば、その項目だけ上書きできる
export const updated = { ...original, age: 21 };

// original はいっさい変わっていない
export const stillOriginal = original;

理解できたか確かめる

確認クイズ

const [count, setCount] = useState(0) の角括弧は何をしている?

確認クイズ

const updated = { ...user, age: 21 } を実行したあと、user はどうなっている?

spread.ts

const original = { name: "さとう", age: 20 };

// ... で中身を展開して、新しいオブジェクトを作る
export const copied = { ...original };

// 展開したあとに書けば、その項目だけ上書きできる
export const updated = { ...original, age: 21 };

// original はいっさい変わっていない
export const stillOriginal = original;

この章のまとめ

  • const { name } = user は分割代入。 オブジェクトは名前、配列は順番で取り出す
  • useState[count, setCount] も、ただの配列の分割代入
  • ... は中身を展開する記号。あとに書いたものが勝つ
  • スプレッド構文は書き換えではなく新しく作るための道具。 React の state 更新はこの形で書く

spread.ts

const original = { name: "さとう", age: 20 };

// ... で中身を展開して、新しいオブジェクトを作る
export const copied = { ...original };

// 展開したあとに書けば、その項目だけ上書きできる
export const updated = { ...original, age: 21 };

// original はいっさい変わっていない
export const stillOriginal = original;