今回は、弊社メンバーが研修時に実装した課題のアウトプットがちょうど良かったので、そこに後からReactとTypeScriptを導入していきます。
この課題は、万葉さんが公開しているものを使っていて、シンプルなTODO管理アプリケーションです。
https://github.com/everyleaf/el-training
既に実装済みのものに乗せていくので、タスク一覧・詳細・編集画面があります。
react-rails gemを使う方法
react-rails
はRailsで実装された、erbやslimなどのビューの中で、Reactコンポーネントを扱えるようにするgemです。
いわゆる、フロントとバックエンドを分離しSPAにするものではなく、画面の一部をリッチにするような用途で使用します。
このあたりの記事を参考にやってみます。
https://qiita.com/sakakinn/items/e9cc99873bd5508c29ed
Gemfile
gem 'react-rails'
$ bundle install $ rails webpacker:install:react
react:installすると何やら生成される。
$ rails generate react:install ... warning "webpack-dev-server > webpack-dev-middleware@3.7.2" has unmet peer dependency "webpack@^4.0.0". append app/javascript/packs/application.js create app/javascript/packs/server_rendering.js
コンポーネントを生成してみる。コンポーネント名(HelloWorld)とProps(greeting:string)を指定する。
$ rails g react:component HelloWorld greeting:string
こんな感じのが生成される
app/javascript/components/HelloWorld.js
import React from "react" import PropTypes from "prop-types" class HelloWorld extends React.Component { render () { return ( <React.Fragment> Greeting: {this.props.greeting} </React.Fragment> ); } } HelloWorld.propTypes = { greeting: PropTypes.string }; export default HelloWorld
適当なViewでコンポーネントを呼び出してみる。今回はタスク一覧画面で呼び出した。
app/views/tasks/index.html.slim
... = react_component("HelloWorld", { greeting: "Hello from react-rails." })
無事表示されました。
react-rails gemを使わない方法
上の実装の場合、基本的にサーバとの通信はRailsを介さなければならないので、Railsとガッツリ結合している状態です。
フロントとバックエンドを分離することを見越して、react_component
を使わずフロント側だけでReactコンポーネントを呼び出す実装に変えてみます。
application.js
// This file is automatically compiled by Webpack, along with any other files // present in this directory. You're encouraged to place your actual application logic in // a relevant structure within app/javascript and only use these pack files to reference // that code so it'll be compiled. require("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") // Uncomment to copy all static images under ../images to the output folder and reference // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>) // or the `imagePath` JavaScript helper below. // // const images = require.context('../images', true) // const imagePath = (name) => images(name, true) // Support component names relative to this directory: var componentRequireContext = require.context("components", true); var ReactRailsUJS = require("react_ujs"); ReactRailsUJS.useContext(componentRequireContext); import App from '../components/HelloWorld' import React from 'react' import ReactDOM from 'react-dom' document.addEventListener('DOMContentLoaded', () => { const ele = document.getElementById('app') ReactDOM.render( <App greeting="Hello from react" />, ele ) })
application.html.slim
doctype html html head title | ElTraining = csrf_meta_tags = csp_meta_tag = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' body = render 'shared/flash' = render 'shared/header' #app = yield
ちゃんと表示されました!
TypeScriptを導入する
コマンドでインストールします。
$ rails webpacker:install:typescript
HelloWorld.tsxに名前を変更し、ソースコードも修正します。
import React from "react" interface Props { greeting: string } class HelloWorld extends React.Component<Props> { render () { return ( <React.Fragment> Greeting: {this.props.greeting} </React.Fragment> ); } } export default HelloWorld
起動すると怒られる。
Failed to compile. /Users/atsushiharada/source/el-training/app/javascript/components/HelloWorld.tsx ./app/javascript/components/HelloWorld.tsx 1:7-12 [tsl] ERROR in /Users/atsushiharada/source/el-training/app/javascript/components/HelloWorld.tsx(1,8) TS1259: Module '"/Users/atsushiharada/source/el-training/node_modules/@types/react/index"' can only be default-imported using the 'allowSyntheticDefaultImports' flag
tsconfig.jsonに追記する。
{ "compilerOptions": { "esModuleInterop": true } }
できたああああああ!!
一応このソースコードのリポジトリも載せておきます。少し中途半端状態になっているので、上記のソース通りになっていないかもですがご容赦を。