ES6からJavaScriptに加わったWeakMap
ですが、Reactと組み合わせて使ってみても意外と便利かなと思い始めました。
WeakMap
とは
WeakMap
も、単なるオブジェクトやWeakでないMap
と同様に、キーと値の組み合わせを管理するための仕組みです。ただ、
-
WeakMap
がキーにできるのは、関数・DOMノード・配列などを含め、オブジェクトだけ(プリミティブは使えない) - 今あるキーの一覧を取得する方法がない
- キーのオブジェクトがガベージコレクトされると、
WeakMap
内のキー・値ペアも消滅する
というような仕組みになっています。ふつうのMap
と違って、入れておいても「キーが」ガベージコレクションされうる、という意味でのWeakです。
よく言われる使いみち
よくWeakMap
の使う道として言及されるのは、
- jQueryの
.data
と同様な、DOMノードにデータを紐付けるために使う -
WeakMap
インスタンス自体をモジュールに封じきることで、オブジェクトに対してpackage private
的なデータの置き場に使う
などです。確かに、いまのところJavaScriptにprivate
はありませんので、これもこれでありなのは間違いなさそうです。
Reactとの組み合わせ
もちろん、Reactで「DOMノードと値を紐付ける」なんて操作をやっても誰も嬉しくならないです。ただ、Reactの中では破壊的変更に頼らないデータ管理が推奨されていますので、この観点から考えれば、特定のオブジェクトに値を結び付けられるWeakMap
は便利に運用できます。
props
から計算したデータのキャッシュ
時折、render
すべきデータが、props
に対して複雑な計算(典型的な例としては、配列の絞り込み・ソートなど)を行ったものとなる、という場面があります。もちろん、「毎回計算する」でも結果は正しくなるのですが、毎回不要な計算をするオーバーヘッドが積もってきてしまいます。
そして、「state
に計算結果を置いておく」という手段もありますが、それもprops
の更新と同期させるのが煩雑となるということで、Reactの公式ブログでも、「別途でキャッシュの仕組みを入れる」ことを推奨しています。
当該のブログでは、memoize-one
として「同じオブジェクトである間キャッシュする」という仕組みとなっているため、キャッシュ関数自体をコンポーネントインスタンスに紐付ける必要が出てきていますが、WeakMap
であれば全部まとめてキャッシュを管理しても全く問題ないので、関数コンポーネントに対しても運用できます。
イベントハンドラ
Reactでコンポーネントを何階層も重ねていくと、「<Parent>
で特定の動作を起こすためのハンドラ関数を下に引き回していくけれど、実際にハンドラに渡す値が供給されるのは孫コンポーネント」というような場面もよくあります。このような場合、孫コンポーネントの側でonClick={() => doParentAction(param)}
のようにバインドすると、render
ごとに無名関数が作り直しとなって、非効率になります。
ここでも、キーを親から降りてくるdoParentAction
としたWeakMap
を作れば、関数コンポーネントでもキャッシュが可能ですし、破棄の心配もありません。
注意点
上記のように、キャッシュとして「きちんと消えてくれる」WeakMap
が必要な場合、Polyfillでは実現できません。IE 10以下には本物がないので、そこでは動かせないのは要注意です。