Rails+Vite+typescriptで動いてる環境にReactを追加する。
概要
Rails+Viteの環境にあとからReactを追加しました.プラグインはVite4.0以降で導入された@vitejs/plugin-react-swc
を使用します。コンパイルにswcを使っていて、babelベースの@vitejs/plugin-react
よりも「単一スレッドでBabelより20倍高速であり、4コアで70倍高速です。」だそうです。
バージョンはこんな感じです。
Rails: 7.1.1
vite_rails: 3.0.17
typescript: 5.2.2
vite: 4.5.0
@vitejs/plugin-react-swc: 3.5.0
React: 18.2.38
インストール
yarn add react react-dom @vitejs/plugin-react-swc
余談ですが、bin/vite build
で書き出されるpublic/vite
ディレクトリはgitignore
だから、公開時にサーバーサイドでbin/vite build
をすることになります。そうすると全てのモジュールがサーバーサイドで必要になるのでどんな環境でも全てインストールされるように全部dependencies
に入れてます。どうするのが正しいのか自信はありませんが。
viteの設定
// vite.config.ts
import { defineConfig } from 'vite';
import RubyPlugin from 'vite-plugin-ruby';
import react from '@vitejs/plugin-react-swc';
export default defineConfig({
plugins: [RubyPlugin(), react()]
});
@react-refreshの設定
この辺で指摘されてますが、@react-refresh
のランタイムを手動で設定する必要があるらしく、vite-ruby
でそのスクリプトが提供されています。
vite_react_refresh_tag
をapplecation.html.erb
に書いてやればOKです。
<!doctype html>
<html>
<head>
...
<%= vite_client_tag %>
<%= vite_react_refresh_tag %>
...
</head>
この設定を忘れると下記のようなエラーが出ます。
React refresh preamble was not loaded. Something is wrong.
このエラーメッセージで別のものが色々引っかかるので深みにハマりました。ちなみに、タグの代わりに本家の指摘通り下記のようなタグを入れると・・・
<%= vite_client_tag %>
<script type="module">
import RefreshRuntime from 'http://localhost:3036/vite-dev/@react-refresh'
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true
</script>
下記のエラーができます。
Error: React refresh runtime was loaded twice. Maybe you forgot the base path?
読んでないのに2度読まれてると・・・仕組みは追わなかったので理屈は分かりませが、このせいで「既にランタイムの設定はできている」と思い込みさらに時間を浪費しました。
typescript
import React from 'react
の省略
React17以降はimport React from 'react';
を省略できますが、typescriptの警告が出るのでtsconfig.json
に設定を追加します。
{
"compilerOptions": {
...
"jsx": "react",
"allowUmdGlobalAccess": true,
...
}
}
@rails/ujs
今回の環境では@hotwired/turbo
をOFFにしたかったので、link_to
でdeleteメソッドを実現するために@rails/ujs
を使用していますがtypeを認識しません。
Could not find a declaration file for module '@rails/ujs'. '...rails-ujs.js' implicitly has an 'any' type.
Try `npm i --save-dev @types/rails__ujs` if it exists or add a new declaration (.d.ts) file containing `declare module '@rails/ujs';`ts(7016)
@types/rails__ujs
はあるのですが、バージョンが6系までしかなく認識しなかったのでrails-ujs.d.ts
を自分で書きました。
declare const Rails: {
start(): void;
};
declare module '@rails/ujs' {
export default Rails;
}
7系が出たらそちらを使うのが良いかと思います。
Discussion