diff --git a/examples/hello-world/package.json b/examples/hello-world/package.json index 2a7f80f..b30b59b 100644 --- a/examples/hello-world/package.json +++ b/examples/hello-world/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "dayjs": "^1.11.10", "react": "file://../../packages/react/pkg/react", "react-dom": "file://../../packages/react-dom/pkg/react-dom", "vite-plugin-wasm": "^3.3.0" diff --git a/examples/hello-world/pnpm-lock.yaml b/examples/hello-world/pnpm-lock.yaml index 298d563..b6ae9ec 100644 --- a/examples/hello-world/pnpm-lock.yaml +++ b/examples/hello-world/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + dayjs: + specifier: ^1.11.10 + version: 1.11.10 react: specifier: file://../../packages/react/pkg/react version: file:../../packages/react/pkg/react @@ -1003,6 +1006,10 @@ packages: which: 2.0.2 dev: true + /dayjs@1.11.10: + resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==} + dev: false + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index c27e487..6786e83 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1,10 +1,13 @@ - - +import dayjs from 'dayjs' function App() { - return ( -
a
- ) + return ( +
{dayjs().format()}
+ ) +} + +function Comp({children}) { + return {`Hello world, ${children}`} } export default App diff --git a/examples/hello-world/src/main.tsx b/examples/hello-world/src/main.tsx index fa04a49..8f70611 100644 --- a/examples/hello-world/src/main.tsx +++ b/examples/hello-world/src/main.tsx @@ -1,6 +1,6 @@ import {createRoot} from 'react-dom' +import App from './App.tsx' -const comp =

Hello World

const root = createRoot(document.getElementById("root")) -root.render(comp) +root.render() diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs index 0fb1c55..879d46d 100644 --- a/packages/react-reconciler/src/begin_work.rs +++ b/packages/react-reconciler/src/begin_work.rs @@ -7,30 +7,37 @@ use shared::derive_from_js_value; use crate::child_fiber::{mount_child_fibers, reconcile_child_fibers}; use crate::fiber::FiberNode; +use crate::fiber_hooks::FiberHooks; use crate::update_queue::process_update_queue; use crate::work_tags::WorkTag; -pub fn begin_work(work_in_progress: Rc>) -> Option>> { +pub fn begin_work(work_in_progress: Rc>) -> Result>>, JsValue> { let tag = work_in_progress.clone().borrow().tag.clone(); return match tag { - WorkTag::FunctionComponent => None, - WorkTag::HostRoot => update_host_root(work_in_progress.clone()), - WorkTag::HostComponent => update_host_component(work_in_progress.clone()), - WorkTag::HostText => None + WorkTag::FunctionComponent => update_function_component(work_in_progress.clone()), + WorkTag::HostRoot => Ok(update_host_root(work_in_progress.clone())), + WorkTag::HostComponent => Ok(update_host_component(work_in_progress.clone())), + WorkTag::HostText => Ok(None), }; } -pub fn update_host_root( +fn update_function_component( work_in_progress: Rc>, -) -> Option>> { +) -> Result>>, JsValue> { + let fiber_hooks = &mut FiberHooks::new(); + let next_children = Rc::new(fiber_hooks.render_with_hooks(work_in_progress.clone())?); + reconcile_children(work_in_progress.clone(), Some(next_children)); + Ok(work_in_progress.clone().borrow().child.clone()) +} + +fn update_host_root(work_in_progress: Rc>) -> Option>> { process_update_queue(work_in_progress.clone()); let next_children = work_in_progress.clone().borrow().memoized_state.clone(); reconcile_children(work_in_progress.clone(), next_children); work_in_progress.clone().borrow().child.clone() } - -pub fn update_host_component( +fn update_host_component( work_in_progress: Rc>, ) -> Option>> { let work_in_progress = Rc::clone(&work_in_progress); @@ -46,22 +53,16 @@ pub fn update_host_component( work_in_progress.clone().borrow().child.clone() } -pub fn reconcile_children(work_in_progress: Rc>, children: Option>) { +fn reconcile_children(work_in_progress: Rc>, children: Option>) { let work_in_progress = Rc::clone(&work_in_progress); let current = { work_in_progress.borrow().alternate.clone() }; if current.is_some() { // update - work_in_progress.borrow_mut().child = reconcile_child_fibers( - work_in_progress.clone(), - current.clone(), - children, - ) + work_in_progress.borrow_mut().child = + reconcile_child_fibers(work_in_progress.clone(), current.clone(), children) } else { // mount - work_in_progress.borrow_mut().child = mount_child_fibers( - work_in_progress.clone(), - None, - children, - ) + work_in_progress.borrow_mut().child = + mount_child_fibers(work_in_progress.clone(), None, children) } -} \ No newline at end of file +} diff --git a/packages/react-reconciler/src/complete_work.rs b/packages/react-reconciler/src/complete_work.rs index 83d02b7..401b88d 100644 --- a/packages/react-reconciler/src/complete_work.rs +++ b/packages/react-reconciler/src/complete_work.rs @@ -5,13 +5,10 @@ use std::rc::Rc; use wasm_bindgen::JsValue; use web_sys::js_sys::Reflect; -use shared::log; - 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 +25,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 +51,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(); } } @@ -107,10 +118,7 @@ impl CompleteWork { let tag = { work_in_progress.clone().borrow().tag.clone() }; match tag { WorkTag::FunctionComponent => { - log!( - "complete unknown fibler.tag {:?}", - work_in_progress.clone().borrow().tag - ); + self.bubble_properties(work_in_progress.clone()); None } WorkTag::HostRoot => { diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index 1cd90ee..7e33bed 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -179,10 +179,22 @@ impl Debug for FiberRootNode { match current_ref.tag { WorkTag::FunctionComponent => { - write!(f, "{:?}", current.borrow()._type.as_ref().unwrap()); + let current_borrowed = current.borrow(); + write!( + f, + "{:?}(flags:{:?}, subtreeFlags:{:?})", + current_borrowed._type.as_ref().unwrap(), + current_borrowed.flags, + current_borrowed.subtree_flags + ); } WorkTag::HostRoot => { - write!(f, "{:?}(subtreeFlags:{:?})", WorkTag::HostRoot, current_ref.subtree_flags); + write!( + f, + "{:?}(subtreeFlags:{:?})", + WorkTag::HostRoot, + current_ref.subtree_flags + ); } WorkTag::HostComponent => { let current_borrowed = current.borrow(); diff --git a/packages/react-reconciler/src/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs new file mode 100644 index 0000000..74ded5a --- /dev/null +++ b/packages/react-reconciler/src/fiber_hooks.rs @@ -0,0 +1,57 @@ +use std::cell::RefCell; +use std::rc::Rc; + +use wasm_bindgen::{JsCast, JsValue}; +use web_sys::js_sys::Function; + +use shared::log; + +use crate::fiber::FiberNode; + +// +// use wasm_bindgen::JsValue; +// +// use crate::fiber::FiberNode; +// use crate::update_queue::UpdateQueue; +// +// pub struct Hook { +// memoized_state: Option>, +// update_queue: Option>>, +// next: Option>>, +// } +// +pub struct FiberHooks { + currently_rendering_fiber: Option>>, +} + +impl FiberHooks { + pub fn new() -> Self { + FiberHooks { + currently_rendering_fiber: None + } + } + + pub fn render_with_hooks(&mut self, work_in_progress: Rc>) -> Result { + self.currently_rendering_fiber = Some(work_in_progress.clone()); + + let work_in_progress_cloned = work_in_progress.clone(); + { + work_in_progress_cloned.borrow_mut().memoized_state = None; + work_in_progress_cloned.borrow_mut().update_queue = None; + } + + + let current = work_in_progress_cloned.borrow().alternate.clone(); + if current.is_some() { + log!("还未实现update时renderWithHooks"); + } else {} + + let work_in_progress_borrow = work_in_progress_cloned.borrow(); + let _type = work_in_progress_borrow._type.as_ref().unwrap(); + let props = work_in_progress_borrow.pending_props.as_ref().unwrap(); + let component = JsValue::dyn_ref::(_type).unwrap(); + let children = component.call1(&JsValue::null(), props); + children + } +} + diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index 015661c..6d0492b 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -18,6 +18,7 @@ mod begin_work; mod child_fiber; mod complete_work; mod commit_work; +mod fiber_hooks; pub trait HostConfig { fn create_text_instance(&self, content: String) -> Rc; diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 08430be..6188116 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -76,8 +76,15 @@ impl WorkLoop { self.prepare_fresh_stack(Rc::clone(&root)); loop { - self.work_loop(); - break; + match self.work_loop() { + Ok(_) => { + break; + } + Err(e) => { + log!("work_loop error {:?}", e); + self.work_in_progress = None; + } + }; } log!("{:?}", *root.clone().borrow()); @@ -104,13 +111,8 @@ impl WorkLoop { 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 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()); @@ -131,20 +133,22 @@ impl WorkLoop { )); } - fn work_loop(&mut self) { + fn work_loop(&mut self) -> Result<(), JsValue> { while self.work_in_progress.is_some() { - self.perform_unit_of_work(self.work_in_progress.clone().unwrap()); + self.perform_unit_of_work(self.work_in_progress.clone().unwrap())?; } + Ok(()) } - fn perform_unit_of_work(&mut self, fiber: Rc>) { - let next = begin_work(fiber.clone()); + fn perform_unit_of_work(&mut self, fiber: Rc>) -> Result<(), JsValue> { + let next = begin_work(fiber.clone())?; if next.is_none() { - self.complete_unit_of_work(fiber.clone()) + self.complete_unit_of_work(fiber.clone()); } else { self.work_in_progress = Some(next.unwrap()); } + Ok(()) } fn complete_unit_of_work(&mut self, fiber: Rc>) { diff --git a/readme.md b/readme.md index 4bede61..7bbaa5a 100644 --- a/readme.md +++ b/readme.md @@ -16,3 +16,5 @@ [从零实现 React v18,但 WASM 版 - [4] 实现 Render 流程的 beginWork 阶段](https://www.paradeto.com/2024/04/11/big-react-wasm-4/) +[从零实现 React v18,但 WASM 版 - [5] 实现 Render 流程的 completeWork 阶段](https://www.paradeto.com/2024/04/15/big-react-wasm-5/) +