From 2e41924c40bfa570386a6f06adb5e1a045ed1c89 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 19 Apr 2024 11:01:52 +0800 Subject: [PATCH 1/8] blog-7 search --- examples/hello-world/src/App.tsx | 9 ++-- examples/hello-world/src/main.tsx | 3 ++ package.json | 5 ++- packages/react-reconciler/src/begin_work.rs | 32 +++++++-------- packages/react-reconciler/src/fiber_hooks.rs | 18 ++++++++ packages/react-reconciler/src/lib.rs | 19 +++++++++ packages/react/src/current_dispatcher.rs | 43 ++++++++++++++++++++ packages/react/src/lib.rs | 17 ++++++++ 8 files changed, 124 insertions(+), 22 deletions(-) create mode 100644 packages/react-reconciler/src/fiber_hooks.rs create mode 100644 packages/react/src/current_dispatcher.rs diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index c27e487..0785c0e 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1,10 +1,11 @@ - +import {useState} from 'react' function App() { - return ( -
a
- ) + const a = useState('d') + return ( +
a
+ ) } export default App diff --git a/examples/hello-world/src/main.tsx b/examples/hello-world/src/main.tsx index fa04a49..b5c98ea 100644 --- a/examples/hello-world/src/main.tsx +++ b/examples/hello-world/src/main.tsx @@ -1,5 +1,8 @@ import {createRoot} from 'react-dom' +import {useState} from 'react/index' +const [a, setA] = useState(); +console.log(a, setA); const comp =

Hello World

const root = createRoot(document.getElementById("root")) root.render(comp) diff --git a/package.json b/package.json index 9d26db3..b2182e5 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,10 @@ "example": "examples" }, "scripts": { - "build:react": "wasm-pack build packages/react --out-dir pkg/react --out-name jsx-dev-runtime", + "build:react-jsx": "wasm-pack build packages/react --out-dir pkg/react --out-name jsx-dev-runtime", + "build:react": "wasm-pack build packages/react --out-dir pkg/react --out-name index", "build:react-dom": "wasm-pack build packages/react-dom --out-dir pkg/react-dom --out-name index", - "build": "npm run build:react && npm run build:react-dom" + "build": "npm run build:react-jsx && npm run build:react && npm run build:react-dom" }, "author": "", "license": "ISC" diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs index 0fb1c55..f65e4b5 100644 --- a/packages/react-reconciler/src/begin_work.rs +++ b/packages/react-reconciler/src/begin_work.rs @@ -13,14 +13,14 @@ use crate::work_tags::WorkTag; pub fn begin_work(work_in_progress: Rc>) -> Option>> { let tag = work_in_progress.clone().borrow().tag.clone(); return match tag { - WorkTag::FunctionComponent => None, + WorkTag::FunctionComponent => update_function_component(work_in_progress.clone()), WorkTag::HostRoot => update_host_root(work_in_progress.clone()), WorkTag::HostComponent => update_host_component(work_in_progress.clone()), - WorkTag::HostText => None + WorkTag::HostText => None, }; } -pub fn update_host_root( +fn update_function_component( work_in_progress: Rc>, ) -> Option>> { process_update_queue(work_in_progress.clone()); @@ -29,8 +29,14 @@ pub fn update_host_root( 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 +52,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/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs new file mode 100644 index 0000000..3064c42 --- /dev/null +++ b/packages/react-reconciler/src/fiber_hooks.rs @@ -0,0 +1,18 @@ +use std::cell::RefCell; +use std::rc::Rc; + +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 { + work_in_progress_hook: Option>>, + currently_rendering_fiber: Option>, +} \ No newline at end of file diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index 015661c..8d1589c 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -4,6 +4,9 @@ use std::rc::Rc; use wasm_bindgen::JsValue; +use react::current_dispatcher::{CURRENT_DISPATCHER, Dispatcher}; +use shared::log; + use crate::fiber::{FiberNode, FiberRootNode, StateNode}; use crate::update_queue::{create_update, enqueue_update}; use crate::work_loop::WorkLoop; @@ -18,6 +21,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; @@ -40,6 +44,7 @@ impl Reconciler { 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))); + self.update_current_dispatcher(); root.clone() } @@ -50,6 +55,20 @@ impl Reconciler { let mut work_loop = WorkLoop::new(self.host_config.clone()); work_loop.schedule_update_on_fiber(host_root_fiber); } + + pub fn update_current_dispatcher(&self) { + unsafe { + let use_state = || { + log!("use_state"); + vec![JsValue::null(), JsValue::null()] + }; + let use_callback = || { + log!("use_callback"); + }; + let b = Rc::new(Dispatcher::new(&use_state, &use_callback)); + CURRENT_DISPATCHER.current = Some(b); + } + } } diff --git a/packages/react/src/current_dispatcher.rs b/packages/react/src/current_dispatcher.rs new file mode 100644 index 0000000..61b2a4d --- /dev/null +++ b/packages/react/src/current_dispatcher.rs @@ -0,0 +1,43 @@ +use std::any::Any; +use std::rc::Rc; + +use wasm_bindgen::JsValue; + +use shared::log; + +enum UseStateParams { + Function(Box Box>), + Value(Box), +} + +pub struct Dispatcher { + pub use_state: *const dyn Fn() -> Vec, + pub use_callback: *const dyn Fn(), +} + +unsafe impl Send for Dispatcher {} + +impl Dispatcher { + pub fn new(use_state: *const dyn Fn() -> Vec, use_callback: *const dyn Fn()) -> Self { + Dispatcher { + use_state, + use_callback, + } + } +} + +pub struct CurrentDispatcher { + pub current: Option>, +} + +pub static mut CURRENT_DISPATCHER: CurrentDispatcher = CurrentDispatcher { current: None }; + +pub fn resolve_dispatche() -> Rc { + unsafe { + let dispatcher = CURRENT_DISPATCHER.current.clone(); + if dispatcher.is_none() { + log!("dispatcher doesn't exist") + } + return dispatcher.unwrap().clone(); + } +} \ No newline at end of file diff --git a/packages/react/src/lib.rs b/packages/react/src/lib.rs index 5bdaaab..f914a5a 100644 --- a/packages/react/src/lib.rs +++ b/packages/react/src/lib.rs @@ -3,6 +3,17 @@ use wasm_bindgen::prelude::*; use shared::REACT_ELEMENT_TYPE; +use crate::current_dispatcher::resolve_dispatche; + +pub mod current_dispatcher; + +#[wasm_bindgen(typescript_custom_section)] +const TS_TYPE: &'static str = r#" +export type Action = State | ((prevState: State) => State); +export type Disptach = (action: Action) => void; +export type useState = (initialState: (() => State) | State) => [State, Disptach]; +"#; + #[wasm_bindgen(js_name = jsxDEV)] pub fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { let react_element = Object::new(); @@ -34,3 +45,9 @@ pub fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { Reflect::set(&react_element, &"props".into(), &props).expect("props panic"); react_element.into() } + +#[wasm_bindgen(js_name = useState, skip_typescript)] +pub unsafe fn use_state() -> Vec { + let dispatcher = resolve_dispatche(); + (&*dispatcher.as_ref().use_state)() +} \ No newline at end of file From c5f06996e66269c2fa384f288cc4cbdcdba6c284 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 19 Apr 2024 14:30:46 +0800 Subject: [PATCH 2/8] blog-7 Support Function Component --- .gitignore | 1 + examples/hello-world/package.json | 4 +- examples/hello-world/pnpm-lock.yaml | 20 +++---- examples/hello-world/src/App.tsx | 6 +- examples/hello-world/src/main.tsx | 7 +-- package.json | 5 +- packages/react-reconciler/src/begin_work.rs | 19 ++++--- packages/react-reconciler/src/fiber_hooks.rs | 59 ++++++++++++++++---- packages/react-reconciler/src/work_loop.rs | 21 +++++-- scripts/build.js | 18 ++++++ 10 files changed, 109 insertions(+), 51 deletions(-) create mode 100644 scripts/build.js diff --git a/.gitignore b/.gitignore index da81e73..13fecbd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ target +dist **/node_modules/** **/.idea/** **/.github/** diff --git a/examples/hello-world/package.json b/examples/hello-world/package.json index 2a7f80f..d5d0e4e 100644 --- a/examples/hello-world/package.json +++ b/examples/hello-world/package.json @@ -10,8 +10,8 @@ "preview": "vite preview" }, "dependencies": { - "react": "file://../../packages/react/pkg/react", - "react-dom": "file://../../packages/react-dom/pkg/react-dom", + "react": "file://../../dist/react", + "react-dom": "file://../../dist/react-dom", "vite-plugin-wasm": "^3.3.0" }, "devDependencies": { diff --git a/examples/hello-world/pnpm-lock.yaml b/examples/hello-world/pnpm-lock.yaml index 298d563..335f1dd 100644 --- a/examples/hello-world/pnpm-lock.yaml +++ b/examples/hello-world/pnpm-lock.yaml @@ -6,11 +6,11 @@ settings: dependencies: react: - specifier: file://../../packages/react/pkg/react - version: file:../../packages/react/pkg/react + specifier: file://../../dist/react + version: file:../../dist/react react-dom: - specifier: file://../../packages/react-dom/pkg/react-dom - version: file:../../packages/react-dom/pkg/react-dom + specifier: file://../../dist/react-dom + version: file:../../dist/react-dom vite-plugin-wasm: specifier: ^3.3.0 version: 3.3.0(vite@5.2.2) @@ -1832,12 +1832,12 @@ packages: engines: {node: '>=10'} dev: true - file:../../packages/react-dom/pkg/react-dom: - resolution: {directory: ../../packages/react-dom/pkg/react-dom, type: directory} - name: react-dom + file:../../dist/react: + resolution: {directory: ../../dist/react, type: directory} + name: react dev: false - file:../../packages/react/pkg/react: - resolution: {directory: ../../packages/react/pkg/react, type: directory} - name: react + file:../../dist/react-dom: + resolution: {directory: ../../dist/react-dom, type: directory} + name: react-dom dev: false diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index 0785c0e..c29f2a9 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1,10 +1,6 @@ -import {useState} from 'react' - - function App() { - const a = useState('d') return ( -
a
+
App
) } diff --git a/examples/hello-world/src/main.tsx b/examples/hello-world/src/main.tsx index b5c98ea..8f70611 100644 --- a/examples/hello-world/src/main.tsx +++ b/examples/hello-world/src/main.tsx @@ -1,9 +1,6 @@ import {createRoot} from 'react-dom' -import {useState} from 'react/index' +import App from './App.tsx' -const [a, setA] = useState(); -console.log(a, setA); -const comp =

Hello World

const root = createRoot(document.getElementById("root")) -root.render(comp) +root.render() diff --git a/package.json b/package.json index b2182e5..e1aa8ff 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,7 @@ "example": "examples" }, "scripts": { - "build:react-jsx": "wasm-pack build packages/react --out-dir pkg/react --out-name jsx-dev-runtime", - "build:react": "wasm-pack build packages/react --out-dir pkg/react --out-name index", - "build:react-dom": "wasm-pack build packages/react-dom --out-dir pkg/react-dom --out-name index", - "build": "npm run build:react-jsx && npm run build:react && npm run build:react-dom" + "build": "node scripts/build.js" }, "author": "", "license": "ISC" diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs index f65e4b5..879d46d 100644 --- a/packages/react-reconciler/src/begin_work.rs +++ b/packages/react-reconciler/src/begin_work.rs @@ -7,26 +7,27 @@ 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 => update_function_component(work_in_progress.clone()), - WorkTag::HostRoot => update_host_root(work_in_progress.clone()), - WorkTag::HostComponent => update_host_component(work_in_progress.clone()), - WorkTag::HostText => None, + WorkTag::HostRoot => Ok(update_host_root(work_in_progress.clone())), + WorkTag::HostComponent => Ok(update_host_component(work_in_progress.clone())), + WorkTag::HostText => Ok(None), }; } fn update_function_component( 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() +) -> 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>> { diff --git a/packages/react-reconciler/src/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs index 3064c42..74ded5a 100644 --- a/packages/react-reconciler/src/fiber_hooks.rs +++ b/packages/react-reconciler/src/fiber_hooks.rs @@ -1,18 +1,57 @@ use std::cell::RefCell; use std::rc::Rc; -use wasm_bindgen::JsValue; +use wasm_bindgen::{JsCast, JsValue}; +use web_sys::js_sys::Function; + +use shared::log; use crate::fiber::FiberNode; -use crate::update_queue::UpdateQueue; -pub struct Hook { - memoized_state: Option>, - update_queue: Option>>, - next: Option>>, +// +// 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 + } } -pub struct FiberHooks { - work_in_progress_hook: Option>>, - currently_rendering_fiber: Option>, -} \ No newline at end of file diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 08430be..f9a04fa 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()); @@ -131,20 +138,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()) } else { self.work_in_progress = Some(next.unwrap()); } + Ok(()) } fn complete_unit_of_work(&mut self, fiber: Rc>) { diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..87bf28b --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,18 @@ +const {execSync} = require('child_process') +const fs = require('fs') +const path = require('path') + +const cwd = process.cwd() + +execSync(`wasm-pack build packages/react --out-dir ${cwd}/dist/react --out-name jsx-dev-runtime`) +execSync(`wasm-pack build packages/react --out-dir ${cwd}/dist/react --out-name index`) +execSync(`wasm-pack build packages/react-dom --out-dir ${cwd}/dist/react-dom --out-name index`) + + +const packageJsonFilename = `${cwd}/dist/react/package.json` +const packageJson = JSON.parse(fs.readFileSync(packageJsonFilename).toString("utf-8")) + +packageJson.files.push('jsx-dev-runtime.wasm', 'jsx-dev-runtime.js', 'jsx-dev-runtime_bg.js', 'jsx-dev-runtime_bg.wasm') + +fs.writeFileSync(packageJsonFilename, JSON.stringify(packageJson)) + From 6fd45b9cf4eb98ec8b81ee7a690e680af1ebf14c Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 19 Apr 2024 16:13:27 +0800 Subject: [PATCH 3/8] blog-7 Support Function Component - fix bugs --- examples/hello-world/src/App.tsx | 6 +- .../react-reconciler/src/complete_work.rs | 60 +++++++++++-------- packages/react-reconciler/src/work_loop.rs | 12 +--- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index c29f2a9..5c287e0 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1,7 +1,11 @@ function App() { return ( -
App
+
) } +function Comp({children}) { + return Hello world +} + export default App 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/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index f9a04fa..2a5775b 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -81,7 +81,6 @@ impl WorkLoop { break; } Err(e) => { - log!("work_loop error {:?}", e); self.work_in_progress = None; } }; @@ -111,13 +110,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()); @@ -149,7 +143,7 @@ impl WorkLoop { 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()); } From d1789380d2b72fa0bfe7bd0fcf3be932f1edf8b8 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 19 Apr 2024 16:29:25 +0800 Subject: [PATCH 4/8] blog-7 rm unnecessary changes --- .gitignore | 1 - examples/hello-world/package.json | 4 +-- examples/hello-world/pnpm-lock.yaml | 20 +++++------ package.json | 4 ++- packages/react-reconciler/src/fiber.rs | 16 +++++++-- packages/react-reconciler/src/lib.rs | 18 ---------- packages/react/src/current_dispatcher.rs | 43 ------------------------ packages/react/src/lib.rs | 17 ---------- readme.md | 2 ++ 9 files changed, 31 insertions(+), 94 deletions(-) delete mode 100644 packages/react/src/current_dispatcher.rs diff --git a/.gitignore b/.gitignore index 13fecbd..da81e73 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ target -dist **/node_modules/** **/.idea/** **/.github/** diff --git a/examples/hello-world/package.json b/examples/hello-world/package.json index d5d0e4e..2a7f80f 100644 --- a/examples/hello-world/package.json +++ b/examples/hello-world/package.json @@ -10,8 +10,8 @@ "preview": "vite preview" }, "dependencies": { - "react": "file://../../dist/react", - "react-dom": "file://../../dist/react-dom", + "react": "file://../../packages/react/pkg/react", + "react-dom": "file://../../packages/react-dom/pkg/react-dom", "vite-plugin-wasm": "^3.3.0" }, "devDependencies": { diff --git a/examples/hello-world/pnpm-lock.yaml b/examples/hello-world/pnpm-lock.yaml index 335f1dd..298d563 100644 --- a/examples/hello-world/pnpm-lock.yaml +++ b/examples/hello-world/pnpm-lock.yaml @@ -6,11 +6,11 @@ settings: dependencies: react: - specifier: file://../../dist/react - version: file:../../dist/react + specifier: file://../../packages/react/pkg/react + version: file:../../packages/react/pkg/react react-dom: - specifier: file://../../dist/react-dom - version: file:../../dist/react-dom + specifier: file://../../packages/react-dom/pkg/react-dom + version: file:../../packages/react-dom/pkg/react-dom vite-plugin-wasm: specifier: ^3.3.0 version: 3.3.0(vite@5.2.2) @@ -1832,12 +1832,12 @@ packages: engines: {node: '>=10'} dev: true - file:../../dist/react: - resolution: {directory: ../../dist/react, type: directory} - name: react + file:../../packages/react-dom/pkg/react-dom: + resolution: {directory: ../../packages/react-dom/pkg/react-dom, type: directory} + name: react-dom dev: false - file:../../dist/react-dom: - resolution: {directory: ../../dist/react-dom, type: directory} - name: react-dom + file:../../packages/react/pkg/react: + resolution: {directory: ../../packages/react/pkg/react, type: directory} + name: react dev: false diff --git a/package.json b/package.json index e1aa8ff..9d26db3 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "example": "examples" }, "scripts": { - "build": "node scripts/build.js" + "build:react": "wasm-pack build packages/react --out-dir pkg/react --out-name jsx-dev-runtime", + "build:react-dom": "wasm-pack build packages/react-dom --out-dir pkg/react-dom --out-name index", + "build": "npm run build:react && npm run build:react-dom" }, "author": "", "license": "ISC" 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/lib.rs b/packages/react-reconciler/src/lib.rs index 8d1589c..6d0492b 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -4,9 +4,6 @@ use std::rc::Rc; use wasm_bindgen::JsValue; -use react::current_dispatcher::{CURRENT_DISPATCHER, Dispatcher}; -use shared::log; - use crate::fiber::{FiberNode, FiberRootNode, StateNode}; use crate::update_queue::{create_update, enqueue_update}; use crate::work_loop::WorkLoop; @@ -44,7 +41,6 @@ impl Reconciler { 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))); - self.update_current_dispatcher(); root.clone() } @@ -55,20 +51,6 @@ impl Reconciler { let mut work_loop = WorkLoop::new(self.host_config.clone()); work_loop.schedule_update_on_fiber(host_root_fiber); } - - pub fn update_current_dispatcher(&self) { - unsafe { - let use_state = || { - log!("use_state"); - vec![JsValue::null(), JsValue::null()] - }; - let use_callback = || { - log!("use_callback"); - }; - let b = Rc::new(Dispatcher::new(&use_state, &use_callback)); - CURRENT_DISPATCHER.current = Some(b); - } - } } diff --git a/packages/react/src/current_dispatcher.rs b/packages/react/src/current_dispatcher.rs deleted file mode 100644 index 61b2a4d..0000000 --- a/packages/react/src/current_dispatcher.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::any::Any; -use std::rc::Rc; - -use wasm_bindgen::JsValue; - -use shared::log; - -enum UseStateParams { - Function(Box Box>), - Value(Box), -} - -pub struct Dispatcher { - pub use_state: *const dyn Fn() -> Vec, - pub use_callback: *const dyn Fn(), -} - -unsafe impl Send for Dispatcher {} - -impl Dispatcher { - pub fn new(use_state: *const dyn Fn() -> Vec, use_callback: *const dyn Fn()) -> Self { - Dispatcher { - use_state, - use_callback, - } - } -} - -pub struct CurrentDispatcher { - pub current: Option>, -} - -pub static mut CURRENT_DISPATCHER: CurrentDispatcher = CurrentDispatcher { current: None }; - -pub fn resolve_dispatche() -> Rc { - unsafe { - let dispatcher = CURRENT_DISPATCHER.current.clone(); - if dispatcher.is_none() { - log!("dispatcher doesn't exist") - } - return dispatcher.unwrap().clone(); - } -} \ No newline at end of file diff --git a/packages/react/src/lib.rs b/packages/react/src/lib.rs index f914a5a..5bdaaab 100644 --- a/packages/react/src/lib.rs +++ b/packages/react/src/lib.rs @@ -3,17 +3,6 @@ use wasm_bindgen::prelude::*; use shared::REACT_ELEMENT_TYPE; -use crate::current_dispatcher::resolve_dispatche; - -pub mod current_dispatcher; - -#[wasm_bindgen(typescript_custom_section)] -const TS_TYPE: &'static str = r#" -export type Action = State | ((prevState: State) => State); -export type Disptach = (action: Action) => void; -export type useState = (initialState: (() => State) | State) => [State, Disptach]; -"#; - #[wasm_bindgen(js_name = jsxDEV)] pub fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { let react_element = Object::new(); @@ -45,9 +34,3 @@ pub fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { Reflect::set(&react_element, &"props".into(), &props).expect("props panic"); react_element.into() } - -#[wasm_bindgen(js_name = useState, skip_typescript)] -pub unsafe fn use_state() -> Vec { - let dispatcher = resolve_dispatche(); - (&*dispatcher.as_ref().use_state)() -} \ No newline at end of file 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/) + From a3242b067adee4e42aa4be2ca9eabe1556f4d15b Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 19 Apr 2024 17:03:48 +0800 Subject: [PATCH 5/8] blog-7 add dayjs --- examples/hello-world/package.json | 1 + examples/hello-world/pnpm-lock.yaml | 7 +++++++ examples/hello-world/src/App.tsx | 6 ++++-- 3 files changed, 12 insertions(+), 2 deletions(-) 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 5c287e0..6786e83 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1,11 +1,13 @@ +import dayjs from 'dayjs' + function App() { return ( -
+
{dayjs().format()}
) } function Comp({children}) { - return Hello world + return {`Hello world, ${children}`} } export default App From e715f6353c43a82dd8c80cbed0fca77a8c8bc549 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 19 Apr 2024 17:04:39 +0800 Subject: [PATCH 6/8] blog-7 rm scripts --- scripts/build.js | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 scripts/build.js diff --git a/scripts/build.js b/scripts/build.js deleted file mode 100644 index 87bf28b..0000000 --- a/scripts/build.js +++ /dev/null @@ -1,18 +0,0 @@ -const {execSync} = require('child_process') -const fs = require('fs') -const path = require('path') - -const cwd = process.cwd() - -execSync(`wasm-pack build packages/react --out-dir ${cwd}/dist/react --out-name jsx-dev-runtime`) -execSync(`wasm-pack build packages/react --out-dir ${cwd}/dist/react --out-name index`) -execSync(`wasm-pack build packages/react-dom --out-dir ${cwd}/dist/react-dom --out-name index`) - - -const packageJsonFilename = `${cwd}/dist/react/package.json` -const packageJson = JSON.parse(fs.readFileSync(packageJsonFilename).toString("utf-8")) - -packageJson.files.push('jsx-dev-runtime.wasm', 'jsx-dev-runtime.js', 'jsx-dev-runtime_bg.js', 'jsx-dev-runtime_bg.wasm') - -fs.writeFileSync(packageJsonFilename, JSON.stringify(packageJson)) - From c6862c08c54fee75f1ff9f5cd1faafa202bde312 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 19 Apr 2024 17:35:17 +0800 Subject: [PATCH 7/8] blog-7 add log for render error --- packages/react-reconciler/src/work_loop.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 2a5775b..6188116 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -81,6 +81,7 @@ impl WorkLoop { break; } Err(e) => { + log!("work_loop error {:?}", e); self.work_in_progress = None; } }; From e60ec867a92da47de0637fce18efeeac00fe3ad6 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 19 Apr 2024 17:36:02 +0800 Subject: [PATCH 8/8] blog-6 fix bugs