Cookie Consent by Free Privacy Policy Generator 📌 Implement React v18 from Scratch Using WASM and Rust - [8] Support Hooks


✅ Implement React v18 from Scratch Using WASM and Rust - [8] Support Hooks


💡 Newskategorie: Programmierung
🔗 Quelle: dev.to

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:v8

The previous article implemented support for the FunctionComponent type, but it doesn't support Hooks yet. In this article, we'll use useState as an example to explain how to implement it.

If you frequently use React, you may have wondered about this: useState is imported from the react library, but its actual implementation is in react-reconciler. How is that achieved? Does React depend on react-reconciler?

To understand this issue, let's analyze Big React.

First, let's take a look at the entry file for useState:

// react/index.ts
import currentDispatcher, {
    Dispatcher,
    resolveDispatcher
} from './src/currentDispatcher';

export const useState = <State>(initialState: (() => State) | State) => {
    const dispatcher = resolveDispatcher() as Dispatcher;
    return dispatcher.useState<State>(initialState);
};

export const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
    currentDispatcher
};

// react/src/currentDispatcher.ts
...
const currentDispatcher: { current: null | Dispatcher } = {
    current: null
};

export const resolveDispatcher = () => {
    const dispatcher = currentDispatcher.current;

    if (dispatcher === null) {
        console.error('resolve dispatcher时dispatcher不存在');
    }
    return dispatcher;
};

export default currentDispatcher;

The code is straightforward. When useState is executed, the core logic involves calling the useState method on currentDispatcher.current. It's evident that currentDispatcher.current is initially set to null. So, where is it assigned a value? The answer lies in renderWithHooks:

// react-reconciler/src/fiberHooks.ts
export const renderWithHooks = (workInProgress: FiberNode) => {
  ...
  currentDispatcher.current = HooksDispatcherOnMount
  ...
}

Moreover, the currentDispatcher here is not directly imported from react, but from the shared library. And shared ultimately imports __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED from react, which contains the currentDispatcher property:

// react-reconciler/src/fiberHooks.ts
import sharedInternals from 'shared/internals'
const {currentDispatcher} = sharedInternals

// shared/internals.ts
import * as React from 'react'
const internals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
export default internals

// react/index.ts
export const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
  currentDispatcher,
}

So, it forms a dependency relationship like this:

react-dom ---depend on--> react-reconciler ---depend on--> shared ---depend on--> react

During bundling, react and shared are bundled together into a react.js file. When bundling react-dom, react needs to be specified as an external dependency. This means that the resulting react-dom.js file won't include the code for react but will treat it as an external dependency:

react + shared => react.js
react-dom + react-reconciler + shared => react-dom.js

This approach allows for easy replacement of the renderer. For example, if you want to implement react-noop for testing purposes:

react-noop + react-reconciler + shared => react-noop.js

However, it's apparent that WASM builds don't support externals. So, what can be done? Upon reconsideration, it's realized that to meet the requirements mentioned above, two key points need to be addressed:

  • React and renderer code should be bundled separately.
  • The renderer should depend on React and be able to modify the values of variables in React at runtime.

We have already achieved the separation of bundling. Now, to implement the second point, which is modifying a variable's value in one WASM module from another WASM module, we refer to the documentation of wasm-bindgen and discover that besides exporting functions from WASM for JavaScript usage, it's also possible to import functions from JavaScript for WASM to invoke:

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
  fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
  // the alert is from JS
  alert(&format!("Hello, {}!", name));
}

So, we can achieve the modification of a variable's value in one WASM module from another by using JavaScript as an intermediary. The specific approach is as follows:

We export an updateDispatcher method from react to JavaScript, which is used to update CURRENT_DISPATCHER.current in react.

fn derive_function_from_js_value(js_value: &JsValue, name: &str) -> Function {
    Reflect::get(js_value, &name.into()).unwrap().dyn_into::<Function>().unwrap()
}

#[wasm_bindgen(js_name = updateDispatcher)]
pub unsafe fn update_dispatcher(args: &JsValue) {
    let use_state = derive_function_from_js_value(args, "use_state");
    CURRENT_DISPATCHER.current = Some(Box::new(Dispatcher::new(use_state)))
}

Then, we declare the import of this method in react-reconciler (for simplicity, we omitted importing from shared here):

#[wasm_bindgen]
extern "C" {
    fn updateDispatcher(args: &JsValue);
}

During render_with_hooks, the updateDispatcher is called, passing an Object that contains the use_state property:


fn update_mount_hooks_to_dispatcher() {
    let object = Object::new();

    let closure = Closure::wrap(Box::new(mount_state) as Box<dyn Fn(&JsValue) -> Vec<JsValue>>);
    let function = closure.as_ref().unchecked_ref::<Function>().clone();
    closure.forget();
    Reflect::set(&object, &"use_state".into(), &function).expect("TODO: panic set use_state");

    updateDispatcher(&object.into());
}

Finally, we need to insert a piece of code at the top of the bundled react-dom/index_bg.js file to import the updateDispatcher method from react:

import {updateDispatcher} from 'react'

Certainly, this step can be implemented using a script.

To summarize, the above process can be represented simply as:

Image description

The details of this update can be found here.

Let's test it by modifying the hello-world example:

import {useState} from 'react'

function App() {
  const [name, setName] = useState(() => 'ayou')
  setTimeout(() => {
    setName('ayouayou')
  }, 1000)
  return (
    <div>
      <Comp>{name}</Comp>
    </div>
  )
}

function Comp({children}) {
  return (
    <span>
      <i>{`Hello world, ${children}`}</i>
    </span>
  )
}

export default App

The result is shown below:

Image description

It looks strange, right? That's because we haven't fully implemented the update process yet.

So far, we have replicated the Big React v3 version. Please kindly give it a star!

...

✅ Implement React v18 from Scratch Using WASM and Rust - [8] Support Hooks


📈 104.22 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust [16] Implement React Noop


📈 104.04 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [13] Implement Lane and Batch Update


📈 98.38 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [10] Implement Update for Single Node.


📈 96.87 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [11] Implement Event System


📈 96.87 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [12] Implement Update for Multi Node


📈 96.87 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [14] Implement Scheduler


📈 96.87 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [17] Implement Concurrent Mode


📈 96.87 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [18] Implement useRef, useCallback, useMemo


📈 96.87 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [7] Support FunctionComponent Type


📈 89.4 Punkte

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


📈 84.59 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [3] Reconciler & Renderer Design


📈 84.59 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [6] Implementation of Commit Process


📈 84.59 Punkte

✅ Implement React v18 from Scratch Using WASM and Rust - [9] Unit Test with Jest


📈 84.59 Punkte

✅ 45sec scratch game vs 1min scratch game vs 10min scratch game


📈 38.84 Punkte

✅ Implement a Simple Calculator Android App by Reusing Logics in Rust via JavaScript-WASM Interfacing


📈 38.63 Punkte

✅ React Hooks – How to Use the useState & useEffect Hooks in Your Project


📈 36.8 Punkte

✅ React Hooks – How to Use the useState & useEffect Hooks in Your Project


📈 36.8 Punkte

✅ Bloodline of Hooks: Custom Hooks in React for Advanced Devs


📈 36.8 Punkte

✅ Flutter Hooks Tutorial: Flutter Animation Using Hooks (useEffect and useAnimationController)


📈 35.68 Punkte

✅ Vuln: radare2 '/format/wasm/wasm.c' Heap Buffer Overflow Vulnerability


📈 35.25 Punkte

✅ radare2 WASM File wasm.c wasm_dis() Pufferüberlauf


📈 35.25 Punkte

✅ radare2 WASM File wasm.c wasm_dis() memory corruption


📈 35.25 Punkte

✅ Binaryen 1.38.22 wasm/wasm-binary.cpp processFunctions() denial of service


📈 35.25 Punkte

✅ Binaryen 1.38.22 wasm/wasm-binary.cpp processFunctions() memory corruption


📈 35.25 Punkte

✅ Binaryen 1.38.22 wasm/wasm.cpp getFunctionOrNull denial of service


📈 35.25 Punkte

✅ Binaryen 1.38.22 WASM File wasm-binary.cpp getType() denial of service


📈 35.25 Punkte

✅ CVE-2023-27114 | radare2 5.8.3 p/wasm/wasm.c wasm_dis memory corruption (ID 21363)


📈 35.25 Punkte

✅ Rust WebAssembly (wasm) on Arch Linux with Webpack (Rust 1.66)


📈 35.07 Punkte

✅ 10 React Hooks Explained // Plus Build your own from Scratch


📈 34.93 Punkte

✅ Virtual Scrolling in React: Implementation from scratch and using react-window


📈 33.34 Punkte

✅ How to Implement Face Detection in React Native Using React Native Vision Camera


📈 31.16 Punkte

✅ Building Vue3 Component Library from Scratch #7 Using Gulp to Implement On-Demand Import


📈 29.76 Punkte











matomo

Datei nicht gefunden!