前提
この記事では、Rust(cargo)と npm は導入済みを前提としています。 知識的にはRust の公式 WebAssembly チュートリアルを一通り読んだくらいで、今回は Wasm と React どう組み合わせるのよってところを重点的に。
必要なモジュール、コマンドの準備
- 
wasm-pack wasm-pack のホームページを参考にして導入。 
- 
cargo-generate 今回はこの cli ツールで、Rust で WebAssembly の雛形を作成したいと思います。 $ cargo install cargo-generate
- 
wasm32-unknown-unknown ターゲットアーキテクチャ $ rustup target add wasm32-unknown-unknown
WebAssembly プロジェクト作成
※この辺りの手順は基本的に上記の公式チュートリアルとほぼ同じ内容です。
まず、git からテンプレートを引っ張ってきます。
プロジェクト名は適当に設定します。
$ cargo generate --git https://github.com/rustwasm/wasm-pack-template
🤷  Project Name: wasm-react-tutorial
🔧  Creating project called `wasm-react-tutorial`...
✨  Done! New project created /Users/yourname/repos/wasm-react-tutorial
$ wasm-react-tutorial一応、wasm-packコマンドで wasm の生成とそれをラップする js ファイル一式が正常に生成されることを確認しておきます。
$ wasm-pack buildnpm initで、wasm-react-tutorial フォルダ内にフロントエンド部分の雛形を作成します。
$ npm init wasm-app www一旦ここまでを終えると、下記のようなディレクトリ構成になっているはずです(一部省略)。
wasm-react-tutorial/
├── Cargo.lock
├── Cargo.toml
├── LICENSE_APACHE
├── LICENSE_MIT
├── README.md
...
├── pkg
└── www
    ├── LICENSE-APACHE
    ├── LICENSE-MIT
    ├── README.md
    ├── bootstrap.js
    ├── index.html
    ├── index.js
    ├── package-lock.json
    ├── package.json
    └── webpack.config.jswww ディレクトリ内でnpm installして必要なパッケージをインストールしつつ、wasm-pack で生成された pkg ディレクトリについても依存ライブラリとして使用する設定を行います。
# www ディレクトリ内
$ npm install
# www/package.json に下記の記述を追加
  "dependencies": {
    ...
    "wasm-react-tutorial": "file:../pkg"
# もう一回 npm i
$ npm i最後に、www/index.js の import を修正します。
import * as wasm from "wasm-react-tutorial";サーバを立ててみて、正常に動作することを確認します。
$ npm run start
# localhost:8080 にアクセスすると、「Hello, wasm-react-tutorial!」というアラートが表示されます。React と typescript の環境を整える
さて、ここからが本番です。
これ以降の作業ディレクトリは www です。
webpack は既に導入済みなので、まずは typescript を入れます。
$ npm i -D typescript ts-loaderwww/tsconfig.json を作成し、一旦下記のように設定しておきます。
{
  "compilerOptions": {
    "sourceMap": true,
    "baseUrl": "./",
    "target": "es5",
    "module": "esNext",
    "lib": ["es2018", "dom"],
    "jsx": "react",
    "strict": true,
    "rootDirs": [
      "src"
    ],
    "types": [
      "react"
    ],
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "Node",
    "typeRoots": ["types", "node_modules/@types"]
  },
  "include": [
    "src"
  ],
  "exclude": [
    "node_modules"
  ]
}react を入れます。
$ npm i -S react react-dom @types/react @types/react-domwww/src ディレクトリを作り、必要なファイルを作成しつつ、不要な www 直下の index.js を削除します。
$ mkdir src/bootstrap.tsx
$ touch src/index.tsx
$ touch src/App.tsx
$ touch src/index.html
$ rm index.js
$ rm bootstrap.js
$ rm index.htmlsrc/index.html は下記の通り。
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>wasm-react-tutorial</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>src/bootstrap.tsx は下記の通り。
// エントリーポイント。wasm を import するモジュールは非同期で import する必要があるので、
// こうすることで、ソース内のどこでも wasm を import することができるようになる。
import("./index").catch(e => console.error("Error importing `bundle.js`:", e));src/index.tsx は下記の通り。
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(<App />, document.getElementById("root"));src/App.tsx は下記の通り。
import React from "react";
const App = () => {
  return (
    <div>wasm-react-test</div>
  );
};
export default App;続いて、webpack で html もビルドできるようにするhtml-webpack-pluginをインストールします。
$ npm i -D html-webpack-plugin最後に、webpack.config.js を設定します。
const path = require('path');
const htmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
  entry: path.resolve(__dirname, "src/bootstrap.tsx"),
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "bootstrap.js"
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "ts-loader"
      },
    ]
  },
  resolve: {
    extensions: [".ts", ".tsx", ".js"]
  },
  devServer: {
    contentBase: path.resolve(__dirname, "public")
  },
  plugins: [
    new htmlWebpackPlugin({ template: './src/index.html' }),
  ]
};動作確認
まずは wasm 呼び出さずに普通にビルドして dev-server を立ててみます。
# www にて実行
$ npm run start
続いて、wasm の greet()メソッドを読んでみます。
// src/App.tsx
import React from "react";
import * as wasm from "wasm-react-tutorial";
const App = () => {
  wasm.greet();
  return (
    <div>wasm-react-test</div>
  );
};
export default App;
これで自由に React アプリから wasm を import できるようになりました!