はじめに
スプレッド構文はオブジェクトをイミュータブルに扱いたい場面で活躍します。Reduxでは、よく使われる構文の一つかなと思います。既存のソースを見ても、Reducerを作る際にスプレッド構分で書かかれることが多いと思うので、その利用方法と注意点のまとめです。
対象読者
- JS初学者
- Redux初学者
##スプレッド構文とは
MDNからの引用です。
配列式や文字列などの反復可能オブジェクトを、0 個以上の引数 (関数呼び出しの場合) や要素 (配列リテラルの場合) を期待された場所で展開したり、オブジェクト式を、0 個以上のキーと値のペア (オブジェクトリテラルの場合) を期待された場所で展開したりすることができます。
実際の利用方法を見てみましょう。
オブジェクトで利用する
既存のReducerの記述を見ると、Object.assign()
で書かれてるものも見かけますが、以下のような書き方で置き換えできるかなと思います。
const favMusic = {
genre: "rock",
country: "uk"
}
// ① オブジェクトをクローンする
const clone_favMusiv = {
...favMusic
} // ▶︎ { genre: 'rock', country: 'uk' }
// ② オブジェクトをマージして新たなオブジェクトを生成する
const merge_favMusiv = {
...favMusic,
...{age: "90s", genre_detail: "alternative"}
} // ▶︎ { genre: 'rock', country: 'uk', age: '90s', genre_detail: 'alternative' }
// ③ プロパティを追加して新たなオブジェクトを生成する
const add_to_clone_favMusiv = {
...favMusic,
age: "90s"
} // ▶︎ { genre: 'rock', country: 'uk', age: '90s' }
配列で利用する
const hobby = ["running", "game", "camp"];
// ① 配列をクローンする
const clone_hobby = [...hobby]; // ▶︎ ["running", "game", "camp"]
// ② 複数の配列をマージして新たな配列を生成する
const merge_hobby = [...hobby, ...clone_hobby]; // ▶︎ [ 'running', 'game', 'camp', 'running', 'game', 'camp' ]
// ③ 新しい要素を追加して配列を生成する
const add_to_clone_hobby = [...hobby, "fishing", "cooking"]// ▶︎ [ 'running', 'game', 'camp', 'fishing', 'cooking' ]
##スプレッド構文の注意点
① 元のオブジェクトに同名プロパティがある場合
同名プロパティを展開した場合、値は上書きされます。
const favMusic = {
genre: "rock",
country: "uk"
}
// ここでは、genreが上書きされてしまう
const replaceProp_favMusiv = {
...favMusic,
...{genre: "classic"}
} // ▶︎ { genre: 'classic', country: 'uk' }
② ネストしてるオブジェクト/配列
スプレッド構文は、 shallow_copy
のため、以下のような場合、意図しない実装をしてしまう可能性があるので注意が必要です。ここでは、オブジェクトを例に挙げます。
const favMusicDetail = {
genre: "rock",
country: "uk",
artist: {
name: "The Beatles",
age: "60s",
}
}
const shallowCopy = {...favMusicDetail}
// 期待値:False
console.log("参照が異なるか検証", favMusicDetail === shallowCopy); // ▶︎ False
// 期待値:False
console.log("子オブジェクト_参照が異なるか検証", favMusicDetail.artist === shallowCopy.artist); // ▶︎ True
上記を確かめると分かるのですが、子オブジェクトは同じ参照であることが分かります。
続いて値を更新してみましょう。
// 一方のオブジェクトの値を更新してみます。
favMusicDetail.genre = "classic";
favMusicDetail.artist.age = "70s";
console.log("favMusicDetail: ", favMusicDetail, "shallowCopy: ", shallowCopy);
// 子オブジェクトは参照先が同じなので、両方とも値が更新されている
// artice.ageが書き換わってることに注目
// ⬇️
//favMusicDetail: {
// genre: 'classic',
// country: 'uk',
// artist: { name: 'The Beatles', age: '70s' }
//} shallowCopy: {
// genre: 'rock',
// country: 'uk',
// artist: { name: 'The Beatles', age: '70s' }
//}
子のオブジェクトは同じ参照なことがわかります。
そのため、ネストしているオブジェクトを扱う場合は、以下の通り、個別に分割する必要がある
ので注意が必要です。
以下コードを追記して確かめてみると、オブジェクトが正しくクローンされてることが分かると思います。
const cloneDetail = {
...favMusicDetail,
artist: {
...favMusicDetail.artist, // 分割代入は、shallow_copyのため、個別に分割する必要がある。
}
}
// 正しくコピーされてる。
favMusicDetail.artist.age = "80s";
console.log("favMusicDetail: ", favMusicDetail, "cloneDetail: ", cloneDetail);
// ⬇️
//favMusicDetail: {
// genre: 'classic',
// country: 'uk',
// artist: { name: 'The Beatles', age: '80s' }
//} cloneDetail: {
// genre: 'classic',
// country: 'uk',
// artist: { name: 'The Beatles', age: '70s' }
//}