React Material-UIのアイコン一覧を作ったらJSXとちょっと仲良くなれた


概要

Material-UIのビルトインのアイコンを使おうと思った時に、

  • 一覧がない(私が見つけられなかっただけかも知れない)
  • Outlinedとか違いがわかりにくい

というつらさを感じていた中、ある日朝目覚めた時に「今日はアイコンの一覧を作る日だ」となぜか強く感じたので実際に作ってみました。作る過程でJSXについて新しく学ぶことがあったのでそれをまとめます。
できた一覧はこちらです。

ちなみに

私とReactのこれまでは、有志のReactのプロジェクトに参加するぞ!となってざっと入門を読んでいきなり書き始めたようなもの(=時間をかけて体系的に学んだわけではない)なので、もしかしたらすごい今更なことを書いているかも知れません。

importしてるアイコンの中身とは

いつもアイコンを使うときに@material-ui/icons/XXXからimportしているので、@material-ui/iconsに行ったら全部取得できるのでは??と思いimport * as icons from '@material-ui/icons'としてみました。すると、オブジェクト形式でアイコンの全量が取得できました。
このアイコンの中身が何を持っているかconsole.logでのぞいてみると

{
    $$typeof: Symbol(react.memo)
    compare: null
    displayName: "WhereToVoteIcon"
    muiName: "SvgIcon"
    type:
        $$typeof: Symbol(react.forward_ref)
        render: ƒ (props, ref)
}

こんなものを持っていました。それぞれのプロパティがどんな役割を果たすのか全くわかりませんでしたが、renderっていうのを持っているのをみて、これ使えばなんとか描画できる何かなんだな〜と言うことがわかりました。(importしたモジュールなので当然なのですが)

JSXの正体とは

ということで、とりあえず特徴的な$$typeofを調べてみると、「なぜReactのコンポーネントは$$typeofプロパティを持っているのか」という記事にぶつかりました。
ここの冒頭で一気にアイコンの描画に近づくわけなのですが、そのまま例を借りると、つまり

<marquee bgcolor="#ffa7c4">hi</marquee>

こういうJSXを書いている時、実際に行われているのは

React.createElement(
  /* type */ 'marquee',
  /* props */ { bgcolor: '#ffa7c4' },
  /* children */ 'hi'
)

だと言うのです。
ここで、 普段Material UIのアイコンを使う時に書くのはタグ名だけなので、つまりこのimportしたものをcreateElementにぶち込めばいいと言うことだな! と思ったので先ほどconsole.log()でのぞいた中身を渡したところ、無事描画ができました!

$$typeofとは

ここでもう目的は達成したわけですが、せっかくなので$$typeofについて知るべくもう少し読み進めてみましょう。以下要約なので詳しく知りたい方は元記事を参照してください。

  • 攻撃を防ぐため、Reactはテキストをデフォルトでstringにしてescapeしている
  • それだけで必ずしもインジェクション攻撃に勝てるかと言うとそうではない
  • ところで、ReactのElementはデザインとしてはプレーンなオブジェクトである
  • (とはいえ自分でReact.createComponent()するとき必ずそうしろと言うことではない
  • object形式であることにはコンパイラの最適化などいくつかの利点がある
  • だけども、もしstringが想定されているところに、もし万が一恣意的なJSONを渡せる欠陥があったとすると、これはXSS攻撃に対する脆弱性となる
  • ここで$$typeof: Symbol.for('react.element'),が生きてくる
  • 万が一そう言う欠陥があったとしても、JSONにSymbolを入れることは不可能だからである
  • それを利用して、Reactは$$typeofがなかったり不正だったりすると解釈をやめるようになっている

以上、そうだったのか!と言うことでJSXとちょっと仲良くなれた気がしました
最後まで目を通して頂きありがとうございました