From e84fab67dd9f00d5550f66f78b38dea8f7645f65 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 2 Aug 2024 15:12:52 +0800 Subject: [PATCH 1/3] add blog 22 --- readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.md b/readme.md index b56a6a5..7eee2a4 100644 --- a/readme.md +++ b/readme.md @@ -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/) From 5d046f1425c35affadb802d309eb88f2b113e333 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Thu, 8 Aug 2024 15:47:21 +0800 Subject: [PATCH 2/3] update host component props --- packages/react-dom/src/host_config.rs | 47 +++++++++++++++---- packages/react-dom/src/synthetic_event.rs | 5 +- packages/react-noop/src/host_config.rs | 27 +++++++++-- packages/react-reconciler/src/commit_work.rs | 46 ++++++++++-------- .../react-reconciler/src/complete_work.rs | 4 +- packages/react-reconciler/src/lib.rs | 5 +- 6 files changed, 93 insertions(+), 41 deletions(-) diff --git a/packages/react-dom/src/host_config.rs b/packages/react-dom/src/host_config.rs index 90b02d8..a1eac2e 100644 --- a/packages/react-dom/src/host_config.rs +++ b/packages/react-dom/src/host_config.rs @@ -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; @@ -48,6 +50,13 @@ extern "C" { fn hasQueueMicrotask(this: &Global) -> JsValue; } +impl ReactDomHostConfig { + fn commit_text_update(&self, text_instance: Rc, content: &JsValue) { + let text_instance = text_instance.clone().downcast::().unwrap(); + text_instance.set_node_value(Some(to_string(content).as_str())); + } +} + impl HostConfig for ReactDomHostConfig { fn create_text_instance(&self, content: &JsValue) -> Rc { let window = window().expect("no global `window` exists"); @@ -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::().unwrap(), ); Rc::new(Node::from(element)) @@ -112,11 +121,6 @@ impl HostConfig for ReactDomHostConfig { } } - fn commit_text_update(&self, text_instance: Rc, content: &JsValue) { - let text_instance = text_instance.clone().downcast::().unwrap(); - text_instance.set_node_value(Some(to_string(content).as_str())); - } - fn insert_child_to_container( &self, child: Rc, @@ -198,4 +202,29 @@ impl HostConfig for ReactDomHostConfig { closure_clone.borrow_mut().take().unwrap_throw().forget(); } } + + fn commit_update(&self, fiber: Rc>) { + 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::() + .unwrap() + .dyn_ref::() + .unwrap(), + &memoized_props, + ); + } + _ => { + log!("Unsupported update type") + } + }; + } } diff --git a/packages/react-dom/src/synthetic_event.rs b/packages/react-dom/src/synthetic_event.rs index dd89207..fecc212 100644 --- a/packages/react-dom/src/synthetic_event.rs +++ b/packages/react-dom/src/synthetic_event.rs @@ -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::().unwrap() @@ -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 } diff --git a/packages/react-noop/src/host_config.rs b/packages/react-noop/src/host_config.rs index e238515..026f478 100644 --- a/packages/react-noop/src/host_config.rs +++ b/packages/react-noop/src/host_config.rs @@ -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}; @@ -69,6 +71,13 @@ pub fn create_container() -> JsValue { container.into() } +impl ReactNoopHostConfig { + fn commit_text_update(&self, text_instance: Rc, content: &JsValue) { + let text_instance = text_instance.clone().downcast::().unwrap(); + Reflect::set(&text_instance, &"text".into(), content); + } +} + impl HostConfig for ReactNoopHostConfig { fn create_text_instance(&self, content: &JsValue) -> Rc { let obj = Object::new(); @@ -136,11 +145,6 @@ impl HostConfig for ReactNoopHostConfig { children.splice(index as u32, 1, &JsValue::undefined()); } - fn commit_text_update(&self, text_instance: Rc, content: &JsValue) { - let text_instance = text_instance.clone().downcast::().unwrap(); - Reflect::set(&text_instance, &"text".into(), content); - } - fn insert_child_to_container( &self, child: Rc, @@ -209,4 +213,17 @@ impl HostConfig for ReactNoopHostConfig { closure_clone.borrow_mut().take().unwrap_throw().forget(); } } + + fn commit_update(&self, fiber: Rc>) { + 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") + } + } + } } diff --git a/packages/react-reconciler/src/commit_work.rs b/packages/react-reconciler/src/commit_work.rs index be78b39..ea5f388 100644 --- a/packages/react-reconciler/src/commit_work.rs +++ b/packages/react-reconciler/src/commit_work.rs @@ -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; } @@ -284,25 +290,25 @@ fn safely_attach_ref(fiber: Rc>) { } } -fn commit_update(finished_work: Rc>) { - 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>) { +// 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>, root: Rc>) { let first_host_fiber: Rc>>>> = Rc::new(RefCell::new(None)); diff --git a/packages/react-reconciler/src/complete_work.rs b/packages/react-reconciler/src/complete_work.rs index 1ab3eb3..6c473aa 100644 --- a/packages/react-reconciler/src/complete_work.rs +++ b/packages/react-reconciler/src/complete_work.rs @@ -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( ¤t.borrow()._ref, diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index 3b7eb3b..b1f86db 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -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> = None; static mut COMPLETE_WORK: Option = None; @@ -36,7 +36,8 @@ pub trait HostConfig { fn append_initial_child(&self, parent: Rc, child: Rc); fn append_child_to_container(&self, child: Rc, parent: Rc); fn remove_child(&self, child: Rc, container: Rc); - fn commit_text_update(&self, text_instance: Rc, content: &JsValue); + // fn commit_text_update(&self, text_instance: Rc, content: &JsValue); + fn commit_update(&self, fiber: Rc>); fn insert_child_to_container( &self, child: Rc, From b245f177e9e493f9843a3f45b67485d8bd6965b5 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Thu, 8 Aug 2024 15:54:54 +0800 Subject: [PATCH 3/3] fix new base state --- packages/react-reconciler/src/update_queue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-reconciler/src/update_queue.rs b/packages/react-reconciler/src/update_queue.rs index 4c804d3..b3e6c8d 100644 --- a/packages/react-reconciler/src/update_queue.rs +++ b/packages/react-reconciler/src/update_queue.rs @@ -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(); }