Reactコンポーネントを単独で使う

普通の静的なHTMLのサイトの中で、限定的に複雑になる部分だけをReactの小さいアプリとして実装するというパターンを個人的によくやる。その際に、Reactの中でもそれ以外の部分でも使うコンポーネントがあって、どう実装すればいいかと悩んだ。

というのも、普通のJSでの実装をReactで再利用できるように作るのは、初期化とかライフサイクル的に大変だ。あるいはもし、同じコンポーネントをそれぞれの専用に実装すると、お互いの挙動を一貫させるのが大変だし、UIの仕様が変わったときには追従に倍の手間が掛かる。

どうするもんかなと考えると、共有するコンポーネントはReactで実装して、利用する箇所ごとにReactDOM.render()すればいいことに気づいた。単純。

例えば、次のようなコンポーネントを共有したいとする。

import React from 'react'

export default class Disclosure extends React.PureComponent {
  render() {
    return ...
  }
}

Reactアプリの外で利用するには次のようにする。

<div class="js-react-disclosure" data-heading="Section heading">
  <p>Lorem ipsum dolor...</p>
</div>
import React from 'react'
import ReactDOM from 'react-dom'
import Disclosure from '../react/Disclosure'

document.querySelectorAll('.js-react-disclosure').forEach((el) => {
  const heading = el.dataset.heading
  const children = el.innerHTML
  ReactDOM.render(
    <Disclosure heading={heading}>{children}</Disclosure>,
    el,
  )
})

簡単な例はGitHubに上げた

propsの渡し方については、PugでJSONのデータをHTMLにJSON.stringify()して埋め込んでJS側でパースするとかでも良さそう。

全部Reactで(別にVue.jsでもいい)できたらなーとは思うものの、要件によってはそれが効率的でないことも多い。Custom ElementsとShadow DOMが使えれば完璧に解決するんだけど、ポリフィルもバグがあったり欲しい機能が実装できなかったりで厳しそうだし、10年後かなー、あー、みたいなことを最近は永遠に考えてる。コールドスリープしたい。