いつの間にかTurbolinksおじさんと化している気もするのですが、今回はTurbolinksを使っているプロジェクト内にReactで作ったウィジェットを埋め込んだ話をしていきます。
Turbolinks~SSRとは逆向きから
JavaScriptで全ページを構築するSPAではファーストビューが遅いという問題があって、JavaScriptをサーバサイドでも実行するSSRを行うことでファーストビューを改善しています。
一方で、Turbolinksは出発点が「HTMLで書かれたWebページ」で、JavaScriptではページ遷移だけに介入して別なHTMLを生成することで、HTML全体のリロードを回避する、という構成になっています。
ちょっとだけReact
以前に書きましたが、React.jsはページ全体に対して組み込むだけではなく、ページのパーツとして一部だけに組み込むことも可能です。
ライフサイクルを見計らって
ただ、途中でページが入れ替わる以上、普通はあまりする機会の来ない「ページからの撤去」も必要となります。まずは、HTMLの生成・変更・削除に関連するライフサイクルを洗い出してみます。
ライフサイクル | 遷移前のDOM | 遷移後のDOM |
---|---|---|
turbolinks:before-cache |
ここのイベントで変更したあとのものがキャッシュされる | (まだ存在しない) |
turbolinks:before-render |
この直後に画面から消える | 裏画面としてDOM操作が可能 |
turbolinks:render |
(画面から消えている) |
document.body として表示される |
ノーマルコース
新しいページにはめ込むコンポーネントをturbolinks:render
ではめ込むことにした場合、比較的使いやすい感じになります。ただし、いったん表示されてからマウントするので、チラツキが発生します。
セットしたまま遷移すると、ページに存在しないコンポーネントだけが残ってしまったり、(Reactの場合はそこまで問題ないかもしれませんが)イベントが消えたDOMノードだけがキャッシュされたりといった不都合が生じるので、アンマウントが必要となります。turbolinks:before-cache
とturbolinks:before-render
の両方でReactDOM.unmountComponentAtNode
しておけばいいでしょう(ReactDOM.unmountComponentAtNode
は、Reactがマウントされていないエレメントに対して実行しても何も起きません)。
上級コース
turbolinks:before-render
では、遷移先の画面の<body>
が形成されて、e.target.newBody
としてイベントからも取れるので、ここでマウントすれば、表示前にReactを構築してしまうことができます。
ただ、マウントされた<body>
はdocument.body
ではないので、Reactの中からdocument.getElementById
などのようなことをしている場合、うまく動かなくなるのは要注意です。