概要
create-react-app は React で開発を始めるのに便利 だが、重い計算をバックグラウンドで行いたくなったので WebWorker を使おうとしたら サポートされていない ことがわかった。WebWorker 自体 onmessage/postMessage のような低レベル API を使用しないといけないようで型の恩恵を受けづらいのが難だなと調べていたら dominique-mueller/create-react-app-typescript-web-worker-setup を見つけた。だいたいこのとおりでうまくいった。
ReferenceError: $RefreshReg$ is not defined
しばらく悩まされたランタイムエラーがこれ。worker 内で起こっていて、シンボルの名前で調べてみると react-refresh に関係している らしい。トランスパイル後の JavaScript なんて読めないよと思いつつ何に対応して reference が生成されているか追っていったら 関数名が大文字で始まっている のを見つけた。まあ関係ないだろうと思いつつ直しておくとエラーがなくなった…。
たしかに React コンポーネントは大文字から始まるし、と react-refresh を見返すと isComponentishName 判定を通ったものは他のシンボルと違う処理が入っているらしい。
手順
詳しくは dominique-mueller/create-react-app-typescript-web-worker-setup を。
準備
- react-app-rewired, worker-loader を入れる:
package.json
- react-app-rewired の設定を行う:
package.json
,react-app-rewired.config.js
- .worker.ts で終わる名前のファイルは worker-loader を使って変換する、というような意味
使用
Worker 側では必要なものを expose する:
import { expose } from 'comlink'
import { f } from 'mylibrary'
export default {} as typeof Worker & { new (): Worker }
expose({f})
使う側では wrap すると Promise が返ってくる:
import { wrap } from 'comlink'
import MyWorker from 'My.worker'
const myWorker = wrap(new MyComlinkWorker()) as { f: (x: number) => Promise<number> }
:
myWorker.f(x).then(g)
TODO
as 以下を書くのが面倒なので any にしてしまっている。勝手になんとかしてくれる方法は?
参考
- Comlink, WebWorker については WebWorkerをenjoyableにするComlinkとは何者か に詳しい。
- create-react-app と TypeScript の組み合わせは create-react-appで React + Typescript な環境を構築する にある。