Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blog 23 - Update HostComponent Props #24

Open
wants to merge 3 commits into
base: feat-memo
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 38 additions & 9 deletions packages/react-dom/src/host_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ use std::rc::Rc;

use js_sys::JSON::stringify;
use js_sys::{global, Function, Promise};
use react_reconciler::work_tags::WorkTag;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
use web_sys::{window, Node};
use web_sys::{window, Element, Node};

use react_reconciler::fiber::FiberNode;
use react_reconciler::HostConfig;
use shared::{log, type_of};
use shared::{derive_from_js_value, log, type_of};

use crate::synthetic_event::update_fiber_props;

Expand Down Expand Up @@ -48,6 +50,13 @@ extern "C" {
fn hasQueueMicrotask(this: &Global) -> JsValue;
}

impl ReactDomHostConfig {
fn commit_text_update(&self, text_instance: Rc<dyn Any>, content: &JsValue) {
let text_instance = text_instance.clone().downcast::<Node>().unwrap();
text_instance.set_node_value(Some(to_string(content).as_str()));
}
}

impl HostConfig for ReactDomHostConfig {
fn create_text_instance(&self, content: &JsValue) -> Rc<dyn Any> {
let window = window().expect("no global `window` exists");
Expand All @@ -62,8 +71,8 @@ impl HostConfig for ReactDomHostConfig {
let document = window.document().expect("should have a document on window");
match document.create_element(_type.as_ref()) {
Ok(element) => {
let element = update_fiber_props(
element.clone(),
update_fiber_props(
&element.clone(),
&*props.clone().downcast::<JsValue>().unwrap(),
);
Rc::new(Node::from(element))
Expand Down Expand Up @@ -112,11 +121,6 @@ impl HostConfig for ReactDomHostConfig {
}
}

fn commit_text_update(&self, text_instance: Rc<dyn Any>, content: &JsValue) {
let text_instance = text_instance.clone().downcast::<Node>().unwrap();
text_instance.set_node_value(Some(to_string(content).as_str()));
}

fn insert_child_to_container(
&self,
child: Rc<dyn Any>,
Expand Down Expand Up @@ -198,4 +202,29 @@ impl HostConfig for ReactDomHostConfig {
closure_clone.borrow_mut().take().unwrap_throw().forget();
}
}

fn commit_update(&self, fiber: Rc<RefCell<FiberNode>>) {
let instance = FiberNode::derive_state_node(fiber.clone());
let memoized_props = fiber.borrow().memoized_props.clone();
match fiber.borrow().tag {
WorkTag::HostText => {
let text = derive_from_js_value(&memoized_props, "content");
self.commit_text_update(instance.unwrap(), &text);
}
WorkTag::HostComponent => {
update_fiber_props(
instance
.unwrap()
.downcast::<Node>()
.unwrap()
.dyn_ref::<Element>()
.unwrap(),
&memoized_props,
);
}
_ => {
log!("Unsupported update type")
}
};
}
}
5 changes: 2 additions & 3 deletions packages/react-dom/src/synthetic_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ pub fn init_event(container: JsValue, event_type: String) {
on_click.forget();
}

pub fn update_fiber_props(node: Element, props: &JsValue) -> Element {
pub fn update_fiber_props(node: &Element, props: &JsValue) {
// log!("update_fiber_props {:?}", node);
let js_value = derive_from_js_value(&node, ELEMENT_EVENT_PROPS_KEY);
let element_event_props = if js_value.is_object() {
js_value.dyn_into::<Object>().unwrap()
Expand Down Expand Up @@ -192,6 +193,4 @@ pub fn update_fiber_props(node: Element, props: &JsValue) -> Element {
}
Reflect::set(&node, &ELEMENT_EVENT_PROPS_KEY.into(), &element_event_props)
.expect("TODO: set ELEMENT_EVENT_PROPS_KEY");

node
}
27 changes: 22 additions & 5 deletions packages/react-noop/src/host_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ use std::any::Any;
use std::cell::RefCell;
use std::rc::Rc;

use react_reconciler::work_tags::WorkTag;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
use web_sys::js_sys;
use web_sys::js_sys::JSON::stringify;
use web_sys::js_sys::{global, Array, Function, Object, Promise, Reflect};

use react_reconciler::fiber::FiberNode;
use react_reconciler::HostConfig;
use shared::{derive_from_js_value, log};

Expand Down Expand Up @@ -69,6 +71,13 @@ pub fn create_container() -> JsValue {
container.into()
}

impl ReactNoopHostConfig {
fn commit_text_update(&self, text_instance: Rc<dyn Any>, content: &JsValue) {
let text_instance = text_instance.clone().downcast::<JsValue>().unwrap();
Reflect::set(&text_instance, &"text".into(), content);
}
}

impl HostConfig for ReactNoopHostConfig {
fn create_text_instance(&self, content: &JsValue) -> Rc<dyn Any> {
let obj = Object::new();
Expand Down Expand Up @@ -136,11 +145,6 @@ impl HostConfig for ReactNoopHostConfig {
children.splice(index as u32, 1, &JsValue::undefined());
}

fn commit_text_update(&self, text_instance: Rc<dyn Any>, content: &JsValue) {
let text_instance = text_instance.clone().downcast::<JsValue>().unwrap();
Reflect::set(&text_instance, &"text".into(), content);
}

fn insert_child_to_container(
&self,
child: Rc<dyn Any>,
Expand Down Expand Up @@ -209,4 +213,17 @@ impl HostConfig for ReactNoopHostConfig {
closure_clone.borrow_mut().take().unwrap_throw().forget();
}
}

fn commit_update(&self, fiber: Rc<RefCell<FiberNode>>) {
match fiber.borrow().tag {
WorkTag::HostText => {
let text = derive_from_js_value(&fiber.borrow().memoized_props, "content");
let instance = FiberNode::derive_state_node(fiber.clone());
self.commit_text_update(instance.unwrap(), &text);
}
_ => {
log!("Unsupported update type")
}
}
}
}
46 changes: 26 additions & 20 deletions packages/react-reconciler/src/commit_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,13 @@ fn commit_mutation_effects_on_fiber(
// finished_work.borrow().alternate
// );
if flags.contains(Flags::Update) {
commit_update(finished_work.clone());
// commit_update(finished_work.clone());
unsafe {
HOST_CONFIG
.as_ref()
.unwrap()
.commit_update(finished_work.clone())
}
finished_work.borrow_mut().flags -= Flags::Update;
}

Expand Down Expand Up @@ -284,25 +290,25 @@ fn safely_attach_ref(fiber: Rc<RefCell<FiberNode>>) {
}
}

fn commit_update(finished_work: Rc<RefCell<FiberNode>>) {
let cloned = finished_work.clone();
match cloned.borrow().tag {
WorkTag::HostText => {
let new_content = derive_from_js_value(&cloned.borrow().pending_props, "content");
let state_node = FiberNode::derive_state_node(finished_work.clone());
log!("commit_update {:?} {:?}", state_node, new_content);
if let Some(state_node) = state_node.clone() {
unsafe {
HOST_CONFIG
.as_ref()
.unwrap()
.commit_text_update(state_node.clone(), &new_content)
}
}
}
_ => log!("commit_update, unsupported type"),
};
}
// fn commit_update(finished_work: Rc<RefCell<FiberNode>>) {
// let cloned = finished_work.clone();
// match cloned.borrow().tag {
// WorkTag::HostText => {
// let new_content = derive_from_js_value(&cloned.borrow().pending_props, "content");
// let state_node = FiberNode::derive_state_node(finished_work.clone());
// log!("commit_update {:?} {:?}", state_node, new_content);
// if let Some(state_node) = state_node.clone() {
// unsafe {
// HOST_CONFIG
// .as_ref()
// .unwrap()
// .commit_text_update(state_node.clone(), &new_content)
// }
// }
// }
// _ => log!("commit_update, unsupported type"),
// };
// }

fn commit_deletion(child_to_delete: Rc<RefCell<FiberNode>>, root: Rc<RefCell<FiberRootNode>>) {
let first_host_fiber: Rc<RefCell<Option<Rc<RefCell<FiberNode>>>>> = Rc::new(RefCell::new(None));
Expand Down
4 changes: 2 additions & 2 deletions packages/react-reconciler/src/complete_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ impl CompleteWork {
match tag {
WorkTag::HostComponent => {
if current.is_some() && work_in_progress_cloned.borrow().state_node.is_some() {
// todo compare
// CompleteWork::mark_update(work_in_progress.clone());
// todo: compare props to decide if need to update
CompleteWork::mark_update(work_in_progress.clone());
let current = current.unwrap();
if !Object::is(
&current.borrow()._ref,
Expand Down
5 changes: 3 additions & 2 deletions packages/react-reconciler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ mod hook_effect_tags;
mod sync_task_queue;
mod update_queue;
mod work_loop;
mod work_tags;
pub mod work_tags;

pub static mut HOST_CONFIG: Option<Rc<dyn HostConfig>> = None;
static mut COMPLETE_WORK: Option<CompleteWork> = None;
Expand All @@ -36,7 +36,8 @@ pub trait HostConfig {
fn append_initial_child(&self, parent: Rc<dyn Any>, child: Rc<dyn Any>);
fn append_child_to_container(&self, child: Rc<dyn Any>, parent: Rc<dyn Any>);
fn remove_child(&self, child: Rc<dyn Any>, container: Rc<dyn Any>);
fn commit_text_update(&self, text_instance: Rc<dyn Any>, content: &JsValue);
// fn commit_text_update(&self, text_instance: Rc<dyn Any>, content: &JsValue);
fn commit_update(&self, fiber: Rc<RefCell<FiberNode>>);
fn insert_child_to_container(
&self,
child: Rc<dyn Any>,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/update_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ pub fn process_update_queue(
}

if new_base_queue_last.is_none() {
new_base_state = result.memoized_state.clone();
new_base_state = new_state.clone();
} else {
new_base_queue_last.clone().unwrap().borrow_mut().next = new_base_queue_last.clone();
}
Expand Down
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@
[从零实现 React v18,但 WASM 版 - [20] 实现 Context](https://www.paradeto.com/2024/07/26/big-react-wasm-20/)

[从零实现 React v18,但 WASM 版 - [21] 性能优化支持 Context](https://www.paradeto.com/2024/07/26/big-react-wasm-21/)

[从零实现 React v18,但 WASM 版 - [22] 实现 memo](https://www.paradeto.com/2024/08/01/big-react-wasm-22/)