Posted at

immerで複雑な構造のオブジェクトを扱う

複雑な構造のオブジェクトに何か値を追加する際、現在の構造を壊さずに値を追加しなければならないのでスプレッド演算子をたくさん使ったコードになりがちです。

イメージとしてはこんな感じのやつです。

const user; // 変更前のユーザー。複雑な構造をしている

const additionalHoge = ['hoge', 'huga']; // 追加したいデータ

const newUser = {
...user,
detail: {
...user.detail,
hoge: [...user.detail.hoge, ...additionalHoge],
}
}

こういう雰囲気のコードをいい感じにリファクタできるimmerというライブラリを最近知りました。mobxの作者の方が作ったライブラリみたいです。

immerを使うと上のやつはこんな感じで書き直せます。

元の変数 user には変更はなく、新たに変更が反映されたオブジェクト newUser が返ってきます。

元の構造を維持するための ...user みたいなコードは一掃出来るのでどこに変化があるのかがわかりやすいです。

import produce from 'immer';

const user; // 変更前のユーザー
const additionalHoge = ['hoge', 'huga']; // 追加したいデータ

const newUser = produce(user, draftUser => {
draftUser.detail.hoge = [...draftUser.detail.hoge, ...additionalHoge];
});

これくらいの構造のネストであれば、そのままjsで書いてもいいと思いますが、ネストが何重にもなればなるほど、immerの見やすさが顕著に出てくると思います。

使いどころとしてはreduxのreducerで使うような例が公式のreadmeにものってます。

他に使えそうと思ったところとして、react-apolloでreact-infinite-scrollerを用いた無限スクロールを行う際、元のデータにデータを追加するコードがスプレッド演算子だらけになるので、ここでも使えるなあと思いました。

react-apollのこういう感じのコードを綺麗に出来る。参考


return {
...previousResult,
search: {
...previousSearch,
nodes: [...previousNodes, ...currentNodes],
pageInfo: currentSearch.pageInfo,
},
};