Implement React v18 from Scratch Using WASM and Rust - [1] Build the Project

ayou - Apr 7 - - Dev Community

Based on big-react,I am going to implement React v18 core features from scratch using WASM and Rust.

Code Repository:https://github.com/ParadeTo/big-react-wasm

The tag related to this article:v1

Tools

Rust: A secure, efficient, and modern programming language (omitting ten thousand words). You can simply follow the installation instructions provided on the official website.

wasm-pack: A one-stop tool for building, testing, and publishing Rust WebAssembly.

cargo-generate: Quickly create Rust projects by using existing Git repositories as templates.

For more information, you can refer to the Rust and WebAssembly.

Project Structure

First, let's set up the following project structure:



.
├── Cargo.toml
├── package.json
├── examples
│   └── hello-world // React project initialized by vite
├── packages
│   ├── react // WASM project created by cargo generate --git https://github.com/rustwasm/wasm-pack-template
│   ├── react-dom // WASM project created by cargo generate --git https://github.com/rustwasm/wasm-pack-template
│   ├── react-reconciler // Rust project created by cargo new
│   └── shared // Rust project created by cargo new


Enter fullscreen mode Exit fullscreen mode

The Cargo.toml file is shown below, similar to the commonly mentioned monorepo architecture in frontend development.



[workspace]

members = [
    "packages/react",
    "packages/react-dom",
    "packages/react-reconciler",
    "packages/shared"
]


Enter fullscreen mode Exit fullscreen mode

Because react and react-dom will export methods for JavaScript to call, we need to create a WASM project using cargo generate --git https://github.com/rustwasm/wasm-pack-template for those two. The other two can be created as regular Rust projects using cargo new.

Setting up the Debugging Environment

Let's delete the code in hello-world/src/main.tsx and write a very simple example:



import {createRoot} from 'react-dom'

const comp = <div>hello world</div>
console.log(comp)
console.log(createRoot(document.getElementById('root')))


Enter fullscreen mode Exit fullscreen mode

When running in the development environment, you can see the compiled code in the browser's debug window as follows:

Image description

Now, our goal is to make the hello-world use the React version we are currently developing. To get the above code to run successfully, we need to do the following steps:

  • Modify the package.json in the hello-world project:


    "react": "file://../../packages/react/pkg/react",
    "react-dom": "file://../../packages/react-dom/pkg/react-dom",


Enter fullscreen mode Exit fullscreen mode
  • Add a packaging command to the package.json in the root directory, using wasm-pack to package react and react-dom into WASM:


  "scripts": {
    "build:react": "wasm-pack build packages/react --out-dir pkg/react --out-name jsx-dev-runtime",
    "build:react-dom": "wasm-pack build packages/react-dom --out-dir pkg/react-dom --out-name index",
    "build": "npm run build:react && npm run build:react-dom"
  },


Enter fullscreen mode Exit fullscreen mode
  • Add the following code to the lib.rs in both react and react-dom:


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

#[wasm_bindgen]
pub fn jsxDEV() -> String {
    "hello world".to_string()
}


Enter fullscreen mode Exit fullscreen mode


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

#[wasm_bindgen]
pub fn createRoot(container: &JsValue) -> JsValue {
    JsValue::null()
}


Enter fullscreen mode Exit fullscreen mode

Since the naming convention in Rust is generally snake_case, it's better to change it to this:



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

#[wasm_bindgen(js_name = jsxDev)]
pub fn jsx_dev() -> String {
    "hello world".to_string()
}


Enter fullscreen mode Exit fullscreen mode


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

#[wasm_bindgen(js_name = createRoot)]
pub fn create_root(container: &JsValue) -> JsValue {
    JsValue::null()
}


Enter fullscreen mode Exit fullscreen mode
  • Run npm run build in the root directory, then run pnpm install and npm run dev in the hello-world directory, open the page in the browser, and you will see the following output:

Image description

This indicates that the method exported by WASM has been successfully called on the JS side, and the debugging environment is set up. However, the slightly troublesome part is that if the code is modified, step 4 needs to be executed again.

In the next article, we will implement the jsx_dev function, so stay tuned.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .