From e57d713cb445510757b2d4a73553dfba5a41ba80 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Tue, 16 Apr 2024 20:16:49 +0800 Subject: [PATCH 1/3] blog-6 --- packages/react-dom/src/host_config.rs | 2 +- packages/react-dom/src/lib.rs | 15 +- packages/react-dom/src/renderer.rs | 5 +- packages/react-reconciler/src/child_fiber.rs | 30 ++-- packages/react-reconciler/src/commit_work.rs | 136 +++++++++++++++++++ packages/react-reconciler/src/fiber.rs | 4 +- packages/react-reconciler/src/lib.rs | 5 +- packages/react-reconciler/src/work_loop.rs | 43 +++++- 8 files changed, 213 insertions(+), 27 deletions(-) create mode 100644 packages/react-reconciler/src/commit_work.rs diff --git a/packages/react-dom/src/host_config.rs b/packages/react-dom/src/host_config.rs index 57a311f..a138d0c 100644 --- a/packages/react-dom/src/host_config.rs +++ b/packages/react-dom/src/host_config.rs @@ -39,6 +39,6 @@ impl HostConfig for ReactDomHostConfig { } fn append_child_to_container(&self, child: Rc, parent: Rc) { - todo!() + self.append_initial_child(parent, child) } } \ No newline at end of file diff --git a/packages/react-dom/src/lib.rs b/packages/react-dom/src/lib.rs index a5c3771..0e4c3d6 100644 --- a/packages/react-dom/src/lib.rs +++ b/packages/react-dom/src/lib.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use wasm_bindgen::prelude::*; +use web_sys::Node; use react_reconciler::Reconciler; @@ -8,15 +9,21 @@ use crate::host_config::ReactDomHostConfig; use crate::renderer::Renderer; use crate::utils::set_panic_hook; -mod utils; -mod renderer; mod host_config; +mod renderer; +mod utils; #[wasm_bindgen(js_name = createRoot)] pub fn create_root(container: &JsValue) -> Renderer { set_panic_hook(); let reconciler = Reconciler::new(Rc::new(ReactDomHostConfig)); - let root = reconciler.create_container(container); + let node = match container.clone().dyn_into::() { + Ok(node) => node, + Err(_) => { + panic!("container should be Node") + } + }; + let root = reconciler.create_container(Rc::new(node)); let renderer = Renderer::new(root, reconciler); renderer -} \ No newline at end of file +} diff --git a/packages/react-dom/src/renderer.rs b/packages/react-dom/src/renderer.rs index e744b61..10d5d9d 100644 --- a/packages/react-dom/src/renderer.rs +++ b/packages/react-dom/src/renderer.rs @@ -23,6 +23,7 @@ impl Renderer { #[wasm_bindgen] impl Renderer { pub fn render(&self, element: &JsValue) { - self.reconciler.update_container(Rc::new(element.clone()), self.root.clone()) + self.reconciler + .update_container(Rc::new(element.clone()), self.root.clone()) } -} \ No newline at end of file +} diff --git a/packages/react-reconciler/src/child_fiber.rs b/packages/react-reconciler/src/child_fiber.rs index 349fc9d..63f220c 100644 --- a/packages/react-reconciler/src/child_fiber.rs +++ b/packages/react-reconciler/src/child_fiber.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use wasm_bindgen::JsValue; use web_sys::js_sys::{Object, Reflect}; -use shared::{derive_from_js_value, log, REACT_ELEMENT_TYPE}; +use shared::{derive_from_js_value, REACT_ELEMENT_TYPE}; use crate::fiber::FiberNode; use crate::fiber_flags::Flags; @@ -64,23 +64,23 @@ fn _reconcile_child_fibers( should_track_effect, )); } else if new_child.is_object() { - log!("{:?}", new_child); - let _typeof = Rc::clone(&derive_from_js_value(new_child.clone(), "$$typeof").unwrap()) - .as_string() - .unwrap(); - if _typeof == REACT_ELEMENT_TYPE { - return Some(place_single_child( - reconcile_single_element( - return_fiber, - current_first_child, - Some(new_child.clone()), - ), - should_track_effect, - )); + if let Some(_typeof) = + Rc::clone(&derive_from_js_value(new_child.clone(), "$$typeof").unwrap()).as_string() + { + if _typeof == REACT_ELEMENT_TYPE { + return Some(place_single_child( + reconcile_single_element( + return_fiber, + current_first_child, + Some(new_child.clone()), + ), + should_track_effect, + )); + } } } } - log!("Unsupported child type when reconcile"); + todo!("Unsupported child type when reconcile"); return None; } diff --git a/packages/react-reconciler/src/commit_work.rs b/packages/react-reconciler/src/commit_work.rs new file mode 100644 index 0000000..6b5ac77 --- /dev/null +++ b/packages/react-reconciler/src/commit_work.rs @@ -0,0 +1,136 @@ +use std::any::Any; +use std::cell::RefCell; +use std::rc::Rc; + +use crate::fiber::{FiberNode, StateNode}; +use crate::fiber_flags::{Flags, get_mutation_mask}; +use crate::HostConfig; +use crate::work_tags::WorkTag; + +pub struct CommitWork { + next_effect: Option>>, + host_config: Rc, +} + +impl CommitWork { + pub fn new(host_config: Rc) -> Self { + Self { + next_effect: None, + host_config, + } + } + pub fn commit_mutation_effects(&mut self, finished_work: Rc>) { + self.next_effect = Some(finished_work); + while self.next_effect.is_some() { + let next_effect = self.next_effect.clone().unwrap().clone(); + let child = next_effect.borrow().child.clone(); + if child.is_some() + && get_mutation_mask().contains(next_effect.borrow().subtree_flags.clone()) + { + self.next_effect = child; + } else { + while self.next_effect.is_some() { + self.commit_mutation_effects_on_fiber(self.next_effect.clone().unwrap()); + let sibling = self + .next_effect + .clone() + .clone() + .unwrap() + .borrow() + .sibling + .clone(); + if sibling.is_some() { + self.next_effect = sibling; + break; + } + + let _return = self + .next_effect + .clone() + .unwrap() + .clone() + .borrow() + ._return + .clone(); + + if _return.is_none() { + self.next_effect = None + } else { + self.next_effect = _return; + } + } + } + } + } + + fn commit_mutation_effects_on_fiber(&self, finished_work: Rc>) { + let flags = finished_work.clone().borrow().flags.clone(); + if flags.contains(Flags::Placement) { + self.commit_placement(finished_work.clone()); + finished_work.clone().borrow_mut().flags -= Flags::Placement + } + } + + fn commit_placement(&self, finished_work: Rc>) { + let host_parent = self.get_host_parent(finished_work.clone()); + if host_parent.is_none() { + return; + } + let parent_state_node = FiberNode::derive_state_node(host_parent.unwrap()); + + if parent_state_node.is_some() { + self.append_placement_node_into_container( + finished_work.clone(), + parent_state_node.unwrap(), + ); + } + } + + fn get_element_from_state_node(&self, state_node: Rc) -> Rc { + match &*state_node { + StateNode::FiberRootNode(root) => root.clone().borrow().container.clone(), + StateNode::Element(ele) => ele.clone(), + } + } + + fn append_placement_node_into_container( + &self, + fiber: Rc>, + parent: Rc, + ) { + let fiber = fiber.clone(); + let tag = fiber.borrow().tag.clone(); + if tag == WorkTag::HostComponent || tag == WorkTag::HostText { + let state_node = fiber.clone().borrow().state_node.clone().unwrap(); + self.host_config.append_child_to_container( + self.get_element_from_state_node(state_node), + parent.clone(), + ); + return; + } + + let child = fiber.borrow().child.clone(); + if child.is_some() { + self.append_placement_node_into_container(child.clone().unwrap(), parent.clone()); + let mut sibling = child.unwrap().clone().borrow().sibling.clone(); + while sibling.is_some() { + self.append_placement_node_into_container(sibling.clone().unwrap(), parent.clone()); + sibling = sibling.clone().unwrap().clone().borrow().sibling.clone(); + } + } + } + + fn get_host_parent(&self, fiber: Rc>) -> Option>> { + let mut parent = fiber.clone().borrow()._return.clone(); + while parent.is_some() { + let p = parent.clone().unwrap(); + let parent_tag = p.borrow().tag.clone(); + if parent_tag == WorkTag::HostComponent || parent_tag == WorkTag::HostRoot { + return Some(p); + } + parent = p.borrow()._return.clone(); + } + + None + } +} diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index 681ff9e..1cd90ee 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -152,13 +152,13 @@ impl FiberNode { } pub struct FiberRootNode { - pub container: Rc, + pub container: Rc, pub current: Rc>, pub finished_work: Option>>, } impl FiberRootNode { - pub fn new(container: Rc, host_root_fiber: Rc>) -> Self { + pub fn new(container: Rc, host_root_fiber: Rc>) -> Self { Self { container, current: host_root_fiber, diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index ed52478..015661c 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -17,6 +17,7 @@ mod work_loop; mod begin_work; mod child_fiber; mod complete_work; +mod commit_work; pub trait HostConfig { fn create_text_instance(&self, content: String) -> Rc; @@ -33,10 +34,10 @@ impl Reconciler { pub fn new(host_config: Rc) -> Self { Reconciler { host_config } } - pub fn create_container(&self, container: &JsValue) -> Rc> { + pub fn create_container(&self, container: Rc) -> Rc> { let host_root_fiber = Rc::new(RefCell::new(FiberNode::new(WorkTag::HostRoot, None, None))); host_root_fiber.clone().borrow_mut().initialize_update_queue(); - let root = Rc::new(RefCell::new(FiberRootNode::new(Rc::new(container.clone()), host_root_fiber.clone()))); + let root = Rc::new(RefCell::new(FiberRootNode::new(container.clone(), host_root_fiber.clone()))); let r1 = root.clone(); host_root_fiber.borrow_mut().state_node = Some(Rc::new(StateNode::FiberRootNode(r1))); root.clone() diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 9fd8a4c..4bb95e4 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -6,8 +6,10 @@ use wasm_bindgen::JsValue; use shared::log; use crate::begin_work::begin_work; +use crate::commit_work::CommitWork; use crate::complete_work::CompleteWork; use crate::fiber::{FiberNode, FiberRootNode, StateNode}; +use crate::fiber_flags::get_mutation_mask; use crate::HostConfig; use crate::work_tags::WorkTag; @@ -84,7 +86,46 @@ impl WorkLoop { } log!("{:?}", *root.clone().borrow()); - // commit + + let finished_work = { + root.clone() + .borrow() + .current + .clone() + .borrow() + .alternate + .clone() + }; + + root.clone().borrow_mut().finished_work = finished_work; + self.commit_root(root); + } + + fn commit_root(&self, root: Rc>) { + let cloned = root.clone(); + if cloned.borrow().finished_work.is_none() { + return; + } + let finished_work = cloned.borrow().finished_work.clone().unwrap(); + cloned.borrow_mut().finished_work = None; + + let subtree_has_effect = get_mutation_mask().contains( + finished_work + .clone() + .borrow() + .subtree_flags + .clone(), + ); + let root_has_effect = + get_mutation_mask().contains(finished_work.clone().borrow().flags.clone()); + + let mut commit_work = &mut CommitWork::new(self.complete_work.host_config.clone()); + if subtree_has_effect || root_has_effect { + commit_work.commit_mutation_effects(finished_work.clone()); + cloned.borrow_mut().current = finished_work.clone(); + } else { + cloned.borrow_mut().current = finished_work.clone(); + } } fn prepare_fresh_stack(&mut self, root: Rc>) { From edf13081cacb3b34647eaafe96265d8072cf4872 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Thu, 18 Apr 2024 11:24:13 +0800 Subject: [PATCH 2/3] blog-6 rm log --- packages/react-dom/src/host_config.rs | 2 +- packages/react-reconciler/src/begin_work.rs | 3 +-- packages/react-reconciler/src/work_loop.rs | 13 ------------- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/packages/react-dom/src/host_config.rs b/packages/react-dom/src/host_config.rs index a138d0c..1e73bd0 100644 --- a/packages/react-dom/src/host_config.rs +++ b/packages/react-dom/src/host_config.rs @@ -32,7 +32,7 @@ impl HostConfig for ReactDomHostConfig { let c = child.clone().downcast::().unwrap(); match p.append_child(&c) { Ok(_) => { - log!("append_initial_child successfully jsvalue {:?} {:?}", p, child); + log!("append_initial_child successfully {:?} {:?}", p, c); } Err(_) => todo!(), } diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs index 5a3765a..0fb1c55 100644 --- a/packages/react-reconciler/src/begin_work.rs +++ b/packages/react-reconciler/src/begin_work.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use wasm_bindgen::JsValue; -use shared::{derive_from_js_value, log}; +use shared::derive_from_js_value; use crate::child_fiber::{mount_child_fibers, reconcile_child_fibers}; use crate::fiber::FiberNode; @@ -25,7 +25,6 @@ pub fn update_host_root( ) -> Option>> { process_update_queue(work_in_progress.clone()); let next_children = work_in_progress.clone().borrow().memoized_state.clone(); - log!("tag {:?}", next_children); reconcile_children(work_in_progress.clone(), next_children); work_in_progress.clone().borrow().child.clone() } diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 4bb95e4..08430be 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -31,11 +31,6 @@ impl WorkLoop { if root.is_none() { return; } - log!( - "schedule_update_on_fiber - root container: {:?}", - root.clone().unwrap().clone().borrow().container - ); - self.ensure_root_is_scheduled(root.unwrap()) } @@ -138,10 +133,6 @@ impl WorkLoop { fn work_loop(&mut self) { while self.work_in_progress.is_some() { - log!( - "work_loop - work_in_progress {:?}", - self.work_in_progress.clone().unwrap().clone().borrow().tag - ); self.perform_unit_of_work(self.work_in_progress.clone().unwrap()); } } @@ -152,10 +143,6 @@ impl WorkLoop { if next.is_none() { self.complete_unit_of_work(fiber.clone()) } else { - log!( - "perform_unit_of_work - next {:?}", - next.clone().unwrap().clone().borrow().tag - ); self.work_in_progress = Some(next.unwrap()); } } From dd529e551a2c6bd8c748001163daa27a726ce551 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 19 Apr 2024 17:36:02 +0800 Subject: [PATCH 3/3] blog-6 fix bugs --- .../react-reconciler/src/complete_work.rs | 53 ++++++++++++------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/packages/react-reconciler/src/complete_work.rs b/packages/react-reconciler/src/complete_work.rs index 83d02b7..203c330 100644 --- a/packages/react-reconciler/src/complete_work.rs +++ b/packages/react-reconciler/src/complete_work.rs @@ -11,7 +11,6 @@ use crate::fiber::{FiberNode, StateNode}; use crate::fiber_flags::Flags; use crate::HostConfig; use crate::work_tags::WorkTag; -use crate::work_tags::WorkTag::HostText; pub struct CompleteWork { pub host_config: Rc, @@ -28,21 +27,24 @@ impl CompleteWork { while node.is_some() { let node_unwrap = node.clone().unwrap(); let n = node_unwrap.clone(); - if n.borrow().tag == WorkTag::HostComponent || n.borrow().tag == HostText { + if n.borrow().tag == WorkTag::HostComponent || n.borrow().tag == WorkTag::HostText { self.host_config.append_initial_child( parent.clone(), FiberNode::derive_state_node(node.clone().unwrap()).unwrap(), ) } else if n.borrow().child.is_some() { let n = node_unwrap.clone(); - let borrowed = n.borrow_mut(); - borrowed - .child - .as_ref() - .unwrap() - .clone() - .borrow_mut() - ._return = Some(node_unwrap.clone()); + { + let borrowed = n.borrow_mut(); + borrowed + .child + .as_ref() + .unwrap() + .clone() + .borrow_mut() + ._return = Some(node_unwrap.clone()); + } + node = node_unwrap.clone().borrow().child.clone(); continue; } @@ -51,30 +53,41 @@ impl CompleteWork { return; } - while node_unwrap.borrow().sibling.clone().is_none() { - if node_unwrap.borrow()._return.is_none() + while node + .clone() + .unwrap() + .clone() + .borrow() + .sibling + .clone() + .is_none() + { + let node_cloned = node.clone().unwrap().clone(); + if node_cloned.borrow()._return.is_none() || Rc::ptr_eq( - &node_unwrap - .borrow() - ._return - .as_ref() - .unwrap(), + &node_cloned.borrow()._return.as_ref().unwrap(), &work_in_progress, ) { return; } - node_unwrap - .borrow_mut() + node = node_cloned.borrow()._return.clone(); + } + + { + node.clone() + .unwrap() + .borrow() .sibling .clone() .unwrap() .clone() .borrow_mut() ._return = node_unwrap.borrow()._return.clone(); - node = node_unwrap.borrow().sibling.clone(); } + + node = node.clone().unwrap().borrow().sibling.clone(); } }