DEV Community

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

Posted on

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

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

We have previously implemented the update process for a single node. This article continues to introduce how to implement updates for multiple nodes.

First, let's look at the begin work stage. The implementation here is similar to the official one. You can refer to the section on multi-node Diff in the previous article. The main difference is that the first round of traversal process has been removed.

It's worth noting that in TypeScript, you can declare a Map type like this:

Map<string | number, Fiber>
Enter fullscreen mode Exit fullscreen mode

However, the key in WASM is of JsValue type. When we try to declare the type like this, it will prompt that JsValue has not implemented Hash and Eq traits:

HashMap<JsValue, Rc<RefCell<FiberNode>>>
Enter fullscreen mode Exit fullscreen mode

So, why not just implement it? Due to the "orphan rule" restriction in Rust, we cannot directly implement these traits for JsValue. We need to create a new struct to wrap it:

struct Key(JsValue);

impl PartialEq for Key {
    fn eq(&self, other: &Self) -> bool {
        Object::is(&self.0, &other.0)
    }
}

impl Eq for Key {}

impl Hash for Key {
    fn hash<H: Hasher>(&self, state: &mut H) {
        if self.0.is_string() {
            self.0.as_string().unwrap().hash(state)
        } else if let Some(n) = self.0.as_f64() {
            n.to_bits().hash(state)
        } else if self.0.is_null() {
            "null".hash(state)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

After the multi-node Diff is completed, the FiberNode that needs to be moved will be marked with Placement, and the parent node of the FiberNode that needs to be deleted will be marked with ChildDeletion. Take the following as an example:

// before
<ul>
  <li key={1}>a</li> // key: "1"
  <li>b</li> // key: 1
  <li>c</li> // key: 2
  <li key={4}>d</li> // key: "4"
</ul>

// after
<ul>
  <li>b</li> // key: 0
  <li>c</li> // key: 1
  <li key={1}>d</li> // key: "1"
</ul>
Enter fullscreen mode Exit fullscreen mode

The results after begin work and complete work are shown below:

Image description

To briefly explain:

  • The input keys are all converted to string types. If no key is passed, the index is used as the key.
  • The li node with key 0 cannot find a node with the same key, so a new node needs to be inserted, marked as Placement.
  • The li node with key 1 can be reused, only need to update its child node content from b to c, marked as Update.
  • The li node with key "1" can be reused, but since oldIndex is less than lastPlacedIndex, it needs to be moved, marked as Placement.

Next, we come to the commit phase, which processes the side effects on the nodes in a depth-first traversal manner.

When executing the insertion operation, it will try to find an insertion point before in siblings. The difficulty is that this insertion point may not be its sibling at the same level. For example, <div/><B/> where B is a FunctionComponent type: function B() {return <div/>}, here before is actually B's child, and the actual level may be deeper. At the same time, if a FiberNode is marked Placement, then it is unstable (its corresponding Element will move in this commit phase), and it cannot be used as before.

If the insertion point before can be found, call parent.insertBefore(node, before), otherwise call parent.appendChild(node).

Using the above example, it will process the side effects on li(0) -> c -> li("1") -> ul in order, and the results will be as follows:

  • commitPlacement

Image description

  • commitUpdate

Image description

  • commitPlacement

Image description

  • commitDeletion

Image description

For details on this update, see here. Please kindly give me a star!

Top comments (0)