Boost」など、さまざまなカンファレンスを企画・運営しています。BOOKS(コードジン・ブックス)は、CodeZineの連載からカットアップした、開発現場の課題解決に役立つ書籍シリーズです。Reactアプリを作成してみよう!本記事では、RailsアプリにReactを組み込む中で、フロントエンド開発の基本的な流れをおさえていきます。Reactは、UI構築に適したコンポーネント指向のJavaScriptフレームワークです。7が登場するまでは、ReactやVueといったJavaScriptフレームワークはバンドラーでアプリケーションに含めるのが普通でした。7では、importmap-railsによってReactモジュールを直接インポートすることが容易になっていますので、ここではこの方法によってReactを導入します。作成するReactアプリはシンプルなものです。コントローラとアクションを1個作成し、そのビューのコンテンツをReactによって生成します。今回はReactによる開発手法の紹介が目的ではありませんので、コンテンツは最低限(メッセージと背景画像が表示されるのみ)にとどめています。土台のアプリケーションを作成するまずは、土台となるRailsアプリケーションを作成します。作成するアプリケーションのライブラリ構成は以下のとおりです(行頭の[○]はデフォルト構成のまま使うもの、[-]はデフォルト構成から外すもの、[+]は加えるもの)。newコマンドで作成します。7から使用できるようになったPropshaftを使ってみることにします。Reactに集中するために同じくJavaScriptのライブラリであるHotwireのインストールをスキップすることにします。アプリケーションの名前はreact_appとします。デフォルトのSprocketsに代わりPropshaftを使いますので、前節で紹介した-aオプションでpropshaftを指定しています。Hotwireのインストールをスキップしますので、--skip-hotwireオプションも指定しています。アプリケーションが作成できたら、続く作業のためにcdコマンドでアプリケーションのフォルダに移動しておきます。react_appこの段階で、フロントエンド関連のファイルがいくつか生成されています。後ほどこれらに手を入れていきますが、どのようなフォルダやファイルがあるかここで見ておくことにしましょう(フロントエンド関連でないフォルダ、ファイルは省略しています)。controllerコマンドで、コントローラをアクションも指定して作成します。コントローラ名はreact、アクション名はhelloとします。hello...略...これで、コントローラapp/controllers/react_controller.js、ビューapp/views/react/hello.html.erbが作成されます。ルートページを、作成したreact#helloアクションに変更しておきましょう。serverコマンドでPumaサーバを起動し、「http://localhost:3000/」にWebブラウザでアクセスして、react#helloが呼び出されるのを確認しておきます。これで土台の準備ができました。importmap-railsを設定するReactは、そのモジュールを直接インポートします。具体的には、importmap-railsに対してモジュールを「ピン留め」していきます。留めとは、論理的なモジュール名をその実体(ローカルのJavaScriptファイルあるいはnpmパッケージなど)に関連付けることをいいます。留めによって、インポートするモジュールを場所やバージョンに依存しない論理的な名前で指定できます。bin/importmapコマンドを使って、以下のようにピン留めします。https://ga.jspm.io/npm:scheduler@0.21.0/index.js今回使うReactのモジュールはreactとreact-dom/clientなので、これらを指定してbin/importmapコマンドを実行すると、自動的にパッケージの場所を割り出し、依存するパッケージも含めてモジュールをピン留めしてくれます。留めした結果は、importmap-railsの設定ファイルであるconfig/importmap.rbに反映されます。上記のbin/importmapコマンド実行後のconfig/importmap.rbは以下のようになっています。"https://ga.jspm.io/npm:scheduler@0.21.0/index.js"これらは全て、ピン留めです。(1)は、デフォルトで用意されているモジュールのピン留め、(2)以降はReact関連モジュールのピン留めです。ここで登場するpin構文は、モジュール名に:toオプションで指定されるモジュール実体をピン留めします(同名の場合は:toオプションは(1)のように省略可能)。:preloadオプションは、モジュールと依存関係にあるモジュールを先に読み込むときtrueに設定します。(2)では、モジュール"https://ga.jspm.io/npm:react@18.0.0/index.js"を"react"という名前にピン留めしてします。この設定により、モジュール名として"react"が指定されたときには、実体として"https://ga.jspm.io/npm:react@18.0.0/index.js"を使うというように対応付けられます。config/importmap.rbに記述したピン留めは、ビューからはjavascript_importmap_tagsヘルパーメソッドの呼び出しでscript要素などに展開されます。JavaScriptからはimport文で参照することができます。具体的にどうなっているかは、本記事の後半で紹介します。Network)からパッケージを検索し、モジュールのURLをピン留めします。Mapsのためのパッケージ管理とコンテンツ配布の機能を提供します。--fromオプションを指定することで、unpkgやjsdelivrといったCDNも選択できます。https://cdn.jsdelivr.net/npm/react@18.0.0/index.js続けて、Reactのコンポーネントを作成していきますが、その前にコンポーネントファイルを入れるフォルダを作成し、そのフォルダもピン留めしておきます。この設定により、コンポーネントも置き場所を気にせずにモジュールとしてインポートすることが容易になります。app/javascriptにcomponentsフォルダを作成し、config/importmap.rbに以下の内容を追記します。"components"pin_all_from構文は、指定したフォルダ以下のJavaScriptファイルを全てピン留めする指定です。:underオプションを指定すると、モジュール名にその値が前置されます。この場合は、"components/xxxx"というようになります。ここでは省略していますが、必要に応じて前述した:preloadオプションを指定できます。Reactコンポーネントを作成するReactモジュールの直接インポートの準備ができましたので、Reactのコンポーネントを作成します。このコンポーネントは、ビュー中の指定するdiv要素に対して、「こんにちは○○さん!」というメッセージを設定するというシンプルなものです。コンポーネントのファイルは、app/javascript/componentsフォルダにreact_hello.jsとして作成します。先ほど設定したpin_all_from構文により、react_hello.jsは論理名"components/react_hello"でインポートできます。(3)})(1)は、コンポーネントで必要なモジュールをインポートしています。このように、ピン留めした論理的な名前でモジュールを指定できます。(2)はHTML(この場合はdiv要素)を生成して返す関数Helloを定義しています。(3)はそれを呼び出してid属性が'app'である要素に戻り値を設定しています。コンポーネントを読み込む指定は、app/javascipt/application.jsに記述します。このファイルは、アプリケーション共通のエントリポイントで、config/importmap.rbの先頭行にあるpin構文によってピン留めされています。そのため、ビュー中のjavascript_importmap_tagsヘルパーメソッドによってscript要素に展開されます。コンポーネントのモジュールは、pin_all_from構文で指定した「components」の付いた名前で指定できます。追加そして、ビューにReactコンポーネントで更新されるdiv要素を追加しておきます。div要素のid属性の値であるappは、コンポーネント中で指定されていますから、両者は一致する必要があります。追加アセットを配置する最後に、アセットである背景画像を配置します。画像ファイルの置き場所であるapp/assets/imagesにpig1.pngを配置し、app/assets/stylesheets/application.cssに以下のようにスタイルを追記します。このファイルは、アプリケーション共通のCSSファイルであり、ビュー中のstylesheet_link_tagヘルパーメソッドによってlink要素に展開されます。url('pig1.png');}再びPumaサーバを起動し、図のようなページが表示されれば成功です。完成したReactアプリケーション[NOTE]JSXは使用できないReactでは、JSX構文でJavaScriptコードの中にHTMLを直接記述することができますが、これにはBabelによるトランスパイルが必要です。importmap-railsを使う場合にはJSX構文は使用できませんので、サンプルではHTMLをJavaScriptコードによって生成しています。JSXを使う例については、jsbundling-railsを使ったバンドラーの回で紹介する予定です。