Laravel等で部分的にReactを利用する際の設計
はじめに
LaravelやRailsなどのフルスタックサーバーサイドフレームワークを利用しており、フロントエンドは未だjQueryやプレーンなJSを利用しているといった現場も多く存在すると思います。
そんな中でReactを導入したいと考えた時、一気に全体を置き換えるのはかなり大変で難しいでしょう。
そこで部分的にReactを導入し、組織として慣れていくための方法を提案します。
前提
テンプレートエンジン上で実装することを想定しているため、ページ全体(丸ごと)でReactを利用できないものとします。これからの話はクライアントサイドのレンダリングのみにフォーカスしたものと考えてください。
ざっくりとした考え方を提供する目的で、ビルドツール等までは言及しません。
具体的な方法
テンプレートエンジン(HTML)上のCSRで問題ないUI単位でReact化を進めていきます。
仮にZennのマイページで考えるとすると、キャプチャ赤枠の記事リストの単位です。
(出典: https://zenn.dev/ )
HTML側
親に当たるHTML要素をReactのマウント先とし、小要素を全て削ります。
その上で、data-react-props
のような形でサーバーサイドフレームワーク側で利用していたデータをdata attribute経由でReactに渡します。
-react
のprefixは必須ではありませんが、本設計とは関係のない既存環境で元々利用されていたdata attibuteと混同しないよう命名規則を決めておくことをお勧めします。
(本当はAPI通信に移行できるとより良いです。今後のリプレイスが捗るため)
Before
<div class="GridContainer">
<article class="ArticleCard">...省略...</article>
<!-- 中略 -->
</div>
After
<div
id="ReactGridContainer"
data-react-props="${view変数経由でjson文字列を渡す}"
></div>
React側
ページごとにentryファイルを用意し、下記のようなコードを書きます。
type GridContainerProps = {} //省略
const APP_ROOT_ID = "ReactGridContainer"
const PROPS_ATTRIBUTE = "data-react-props"
const init = () => {
const rootElement = document.getElementById(APP_ROOT_ID)
// 実際には型の付与や、キャストなどの処理が必要
const props = JSON.parse(rootElement.getAttribute(PROPS_ATTRIBUTE) || "{}") as GridContainerProps;
const root = createRoot(rootElement)
root.render(<GridContainer {...props} />)
}
init();
複数存在する場合は下記のようにRootをマウントして、各UI毎にcreatePortalしても良いかもしれません。
document.body.innerHTML = '<div id="app"></div>';
const root = createRoot(document.getElementById('app'));
// Root内でcreatePortalする
root.render(<Root {...props} />)
最後に
ざっとこんな感じかな、でサンプルコードを書いているので動かなかったらすみません🙏
初めてこれに似た手法に出会ったときにもっと早くに知りたかった...!と思ったため簡略化してとっつきやすい記事にしようと思いました。誰かのお役に立てれば幸いです🤓
Reactの公式ドキュメントにも近い記述があります!
Discussion