Open13

React×RustでWebAssemblyをしてみる

You-sakuYou-saku

プロジェクトを作成する

npx create-react-app react-wasm --template typescript
You-sakuYou-saku

react-wasmディレクトリでRustのプロジェクトを作成

cargo new --lib wasm-lib

この時のディレクトリ構造は下記の通り

react-wasm
├── README.md
├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
├── tsconfig.json
└── wasm-lib // Rustのコード
You-sakuYou-saku

wasm-lib内を書き換える

react-wasm/wasm-lib/src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(left: i32, right: i32) -> i32 {
    return left + right;
}
react-wasm/wasm-lib/Cargo.toml
[package]
name = "wasm-lib"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2"
You-sakuYou-saku

wasm-libディレクトリでRustプロジェクトをビルドします

wasm-pack build --target web

するとpkgディレクトリができます

You-sakuYou-saku

reactプロジェクト内でRustのコードを呼び出せるようにpackage.jsonを変更

package.json
 "dependencies": {
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "@types/jest": "^27.5.2",
    "@types/node": "^16.18.46",
    "@types/react": "^18.2.21",
    "@types/react-dom": "^18.2.7",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "typescript": "^4.9.5",
    "web-vitals": "^2.1.4",
+   "wasm-lib": "file:wasm-lib/pkg"
  },
You-sakuYou-saku

index.tsxApp.tsxを書き換える

react-wasm/src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <App />
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
react-wasm/src/App.tsx
import React from 'react';
import logo from './logo.svg';
import './App.css';
import init, { add } from "wasm-lib"; // .rsの関数(add)をimport

function App() {
  // 呼び出す
  init().then(() => {
    const sum: number = add(1, 2);
    console.log(sum);
  });

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;
You-sakuYou-saku

今後の宿題

  • wasm-packの詳細を理解する
  • initをインポートした理由は?