From a57c1ee191fb89020b767b0ab1c720fe63d0a801 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Fri, 19 Apr 2024 20:53:16 +0800 Subject: [PATCH 1/7] blog-8 try modify CURRENT_DISPATCHER, not work. As react and react-dom will be built into different bundles. The big-react makes react as the external when bundle react-dom. --- examples/hello-world/package.json | 4 +- examples/hello-world/pnpm-lock.yaml | 20 ++++----- examples/hello-world/src/App.tsx | 3 ++ package.json | 4 +- packages/react-reconciler/src/commit_work.rs | 3 ++ packages/react-reconciler/src/lib.rs | 22 ++++++++++ packages/react/src/current_dispatcher.rs | 44 ++++++++++++++++++++ packages/react/src/lib.rs | 26 +++++++++++- scripts/build.js | 17 ++++++++ 9 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 packages/react/src/current_dispatcher.rs create mode 100644 scripts/build.js diff --git a/examples/hello-world/package.json b/examples/hello-world/package.json index b30b59b..f9fd444 100644 --- a/examples/hello-world/package.json +++ b/examples/hello-world/package.json @@ -11,8 +11,8 @@ }, "dependencies": { "dayjs": "^1.11.10", - "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 b6ae9ec..35d363b 100644 --- a/examples/hello-world/pnpm-lock.yaml +++ b/examples/hello-world/pnpm-lock.yaml @@ -9,11 +9,11 @@ dependencies: specifier: ^1.11.10 version: 1.11.10 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) @@ -1839,12 +1839,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 6786e83..a427371 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1,6 +1,9 @@ import dayjs from 'dayjs' +import {useState} from 'react' function App() { + const [a, setA] = useState("a") + return (
{dayjs().format()}
) diff --git a/package.json b/package.json index 9d26db3..e1aa8ff 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,7 @@ "example": "examples" }, "scripts": { - "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" + "build": "node scripts/build.js" }, "author": "", "license": "ISC" diff --git a/packages/react-reconciler/src/commit_work.rs b/packages/react-reconciler/src/commit_work.rs index 6b5ac77..805b731 100644 --- a/packages/react-reconciler/src/commit_work.rs +++ b/packages/react-reconciler/src/commit_work.rs @@ -2,6 +2,8 @@ use std::any::Any; use std::cell::RefCell; use std::rc::Rc; +use shared::log; + use crate::fiber::{FiberNode, StateNode}; use crate::fiber_flags::{Flags, get_mutation_mask}; use crate::HostConfig; @@ -101,6 +103,7 @@ impl CommitWork { let fiber = fiber.clone(); let tag = fiber.borrow().tag.clone(); if tag == WorkTag::HostComponent || tag == WorkTag::HostText { + log!("{:?}", fiber.clone().borrow()._type); 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), diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index 6d0492b..624d026 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; @@ -45,12 +48,31 @@ impl Reconciler { } pub fn update_container(&self, element: Rc, root: Rc>) { + self.update_current_dispatcher(); + let host_root_fiber = Rc::clone(&root).borrow().current.clone(); let update = create_update(element); enqueue_update(host_root_fiber.borrow(), update); 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); + let dispatcher = CURRENT_DISPATCHER.current.clone(); + + log!("dispatcher doesn't exist, {:?}", dispatcher); + } + } } diff --git a/packages/react/src/current_dispatcher.rs b/packages/react/src/current_dispatcher.rs new file mode 100644 index 0000000..4fdb491 --- /dev/null +++ b/packages/react/src/current_dispatcher.rs @@ -0,0 +1,44 @@ +use std::any::Any; +use std::rc::Rc; + +use wasm_bindgen::JsValue; + +use shared::log; + +enum UseStateParams { + Function(Box Box>), + Value(Box), +} + +#[derive(Debug)] +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_dispatcher() -> 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..f1a9a2e 100644 --- a/packages/react/src/lib.rs +++ b/packages/react/src/lib.rs @@ -1,10 +1,26 @@ +use std::rc::Rc; + use js_sys::{Object, Reflect}; use wasm_bindgen::prelude::*; use shared::REACT_ELEMENT_TYPE; +use crate::current_dispatcher::{Dispatcher, resolve_dispatcher}; + +pub mod current_dispatcher; + +struct MyStruct { + current: Option>, +} + +impl MyStruct { + pub fn new(current: Option>) -> Self { + MyStruct { current } + } +} + #[wasm_bindgen(js_name = jsxDEV)] -pub fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { +pub unsafe fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { let react_element = Object::new(); Reflect::set( &react_element, @@ -34,3 +50,11 @@ 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)] +pub unsafe fn use_state() -> Vec { + let dispatcher = resolve_dispatcher(); + (&*dispatcher.as_ref().use_state)() +} + diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..0e8abb2 --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,17 @@ +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)) \ No newline at end of file From 2ba0adce79f03c1f54ebbbefbefcb0a202c2eb62 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Sat, 20 Apr 2024 09:16:33 +0800 Subject: [PATCH 2/7] blog-8 try useState --- examples/hello-world/src/App.tsx | 2 +- packages/react-dom/src/renderer.rs | 1 - packages/react-reconciler/src/begin_work.rs | 14 +- .../src/current_dispatcher.rs | 20 +-- packages/react-reconciler/src/fiber.rs | 9 +- packages/react-reconciler/src/fiber_hooks.rs | 164 ++++++++++++++---- packages/react-reconciler/src/lib.rs | 26 +-- packages/react-reconciler/src/update_queue.rs | 17 +- packages/react/src/lib.rs | 25 +-- 9 files changed, 177 insertions(+), 101 deletions(-) rename packages/{react => react-reconciler}/src/current_dispatcher.rs (50%) diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index a427371..2797ddf 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -3,7 +3,7 @@ import {useState} from 'react' function App() { const [a, setA] = useState("a") - + console.log(a, setA('ayou')) return (
{dayjs().format()}
) diff --git a/packages/react-dom/src/renderer.rs b/packages/react-dom/src/renderer.rs index 10d5d9d..57f4efa 100644 --- a/packages/react-dom/src/renderer.rs +++ b/packages/react-dom/src/renderer.rs @@ -3,7 +3,6 @@ use std::rc::Rc; use wasm_bindgen::JsValue; use wasm_bindgen::prelude::*; -use wasm_bindgen::prelude::wasm_bindgen; use react_reconciler::fiber::FiberRootNode; use react_reconciler::Reconciler; diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs index 879d46d..ae7849a 100644 --- a/packages/react-reconciler/src/begin_work.rs +++ b/packages/react-reconciler/src/begin_work.rs @@ -6,8 +6,8 @@ use wasm_bindgen::JsValue; 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::fiber::{FiberNode, MemoizedState}; +use crate::fiber_hooks::render_with_hooks; use crate::update_queue::process_update_queue; use crate::work_tags::WorkTag; @@ -24,8 +24,7 @@ pub fn begin_work(work_in_progress: Rc>) -> Result>, ) -> Result>>, JsValue> { - let fiber_hooks = &mut FiberHooks::new(); - let next_children = Rc::new(fiber_hooks.render_with_hooks(work_in_progress.clone())?); + let next_children = Rc::new(render_with_hooks(work_in_progress.clone())?); reconcile_children(work_in_progress.clone(), Some(next_children)); Ok(work_in_progress.clone().borrow().child.clone()) } @@ -33,7 +32,12 @@ fn update_function_component( 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); + if next_children.is_none() { + panic!("update_host_root next_children is none") + } + if let MemoizedState::JsValue(next_children) = next_children.unwrap() { + reconcile_children(work_in_progress.clone(), Some(next_children)); + } work_in_progress.clone().borrow().child.clone() } diff --git a/packages/react/src/current_dispatcher.rs b/packages/react-reconciler/src/current_dispatcher.rs similarity index 50% rename from packages/react/src/current_dispatcher.rs rename to packages/react-reconciler/src/current_dispatcher.rs index 4fdb491..9864def 100644 --- a/packages/react/src/current_dispatcher.rs +++ b/packages/react-reconciler/src/current_dispatcher.rs @@ -1,10 +1,7 @@ use std::any::Any; -use std::rc::Rc; use wasm_bindgen::JsValue; -use shared::log; - enum UseStateParams { Function(Box Box>), Value(Box), @@ -12,14 +9,14 @@ enum UseStateParams { #[derive(Debug)] pub struct Dispatcher { - pub use_state: *const dyn Fn() -> Vec, + pub use_state: *const dyn Fn(&JsValue) -> 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 { + pub fn new(use_state: *const dyn Fn(&JsValue) -> Vec, use_callback: *const dyn Fn()) -> Self { Dispatcher { use_state, use_callback, @@ -28,17 +25,10 @@ impl Dispatcher { } pub struct CurrentDispatcher { - pub current: Option>, + pub current: Option>, } pub static mut CURRENT_DISPATCHER: CurrentDispatcher = CurrentDispatcher { current: None }; -pub fn resolve_dispatcher() -> 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-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index 7e33bed..e72dacc 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -11,6 +11,7 @@ use web_sys::js_sys::Reflect; use shared::derive_from_js_value; use crate::fiber_flags::Flags; +use crate::fiber_hooks::Hook; use crate::update_queue::{Update, UpdateQueue, UpdateType}; use crate::work_tags::WorkTag; @@ -20,6 +21,12 @@ pub enum StateNode { Element(Rc), } +#[derive(Debug, Clone)] +pub enum MemoizedState { + JsValue(Rc), + Hook(Rc>), +} + #[derive(Debug)] pub struct FiberNode { pub tag: WorkTag, @@ -35,7 +42,7 @@ pub struct FiberNode { pub flags: Flags, pub subtree_flags: Flags, pub memoized_props: Option>, - pub memoized_state: Option>, + pub memoized_state: Option, } impl FiberNode { diff --git a/packages/react-reconciler/src/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs index 74ded5a..c6d0f14 100644 --- a/packages/react-reconciler/src/fiber_hooks.rs +++ b/packages/react-reconciler/src/fiber_hooks.rs @@ -2,11 +2,14 @@ use std::cell::RefCell; use std::rc::Rc; use wasm_bindgen::{JsCast, JsValue}; +use wasm_bindgen::prelude::{Closure, wasm_bindgen}; use web_sys::js_sys::Function; use shared::log; -use crate::fiber::FiberNode; +use crate::current_dispatcher::{CURRENT_DISPATCHER, Dispatcher}; +use crate::fiber::{FiberNode, MemoizedState}; +use crate::update_queue::UpdateQueue; // // use wasm_bindgen::JsValue; @@ -14,44 +17,147 @@ use crate::fiber::FiberNode; // 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>>, +#[derive(Debug, Clone)] +pub struct Hook { + memoized_state: Option>, + update_queue: Option>>, + next: Option>>, } -impl FiberHooks { - pub fn new() -> Self { - FiberHooks { - currently_rendering_fiber: None +impl Hook { + fn new() -> Self { + Hook { + memoized_state: None, + update_queue: None, + next: None, } } +} - pub fn render_with_hooks(&mut self, work_in_progress: Rc>) -> Result { - self.currently_rendering_fiber = Some(work_in_progress.clone()); +static mut CURRENTLY_RENDERING_FIBER: Option>> = None; +static mut WORK_IN_PROGRESS_HOOK: Option>> = None; - 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; - } +pub fn render_with_hooks(work_in_progress: Rc>) -> Result { + unsafe { 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 current = work_in_progress_cloned.borrow().alternate.clone(); + if current.is_some() { + log!("还未实现update时renderWithHooks"); + } else { + let use_callback = || { + log!("use_callback"); + }; + let b = Box::new(Dispatcher::new(&mount_state, &use_callback)); + unsafe { + CURRENT_DISPATCHER.current = Some(b); + } + } + let _type; + let props; + { 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 + _type = work_in_progress_borrow._type.clone().unwrap(); + props = work_in_progress_borrow.pending_props.clone().unwrap(); } + + let component = JsValue::dyn_ref::(&_type).unwrap(); + let children = component.call1(&JsValue::null(), &props); + children +} + +fn mount_work_in_progress_nook() -> Option>> { + let hook = Rc::new(RefCell::new(Hook::new())); + unsafe { + if WORK_IN_PROGRESS_HOOK.is_none() { + if CURRENTLY_RENDERING_FIBER.is_none() { + log!("WORK_IN_PROGRESS_HOOK and CURRENTLY_RENDERING_FIBER is empty") + } else { + CURRENTLY_RENDERING_FIBER.as_ref().unwrap().clone().borrow_mut().memoized_state = Some(MemoizedState::Hook(hook.clone())); + WORK_IN_PROGRESS_HOOK = Some(hook.clone()); + } + } else { + WORK_IN_PROGRESS_HOOK.as_ref().unwrap().clone().borrow_mut().next = Some(hook.clone()); + WORK_IN_PROGRESS_HOOK = Some(hook.clone()); + } + WORK_IN_PROGRESS_HOOK.clone() + } +} + +fn mount_state(initial_state: &JsValue) -> Vec { + let hook = mount_work_in_progress_nook(); + // let memoizedState: State; + // if (initialState instanceof Function) { + // memoizedState = initialState(); + // } else { + // memoizedState = initialState; + // } + // hook.memoizedState = memoizedState; + // + // if (currentlyRenderingFiber === null) { + // console.error('mountState时currentlyRenderingFiber不存在'); + // } + // const queue = createUpdateQueue(); + // hook.updateQueue = queue; + + // let closure = Closure::wrap(Box::new(|| unsafe { + // // dispatch_set_state1(CURRENTLY_RENDERING_FIBER.clone()); + // log!("closure") + // })); + + let closure = Closure::wrap(Box::new(move |action: &JsValue| unsafe { + // web_sys::console::log_1(&"Hello, world!".into()); + dispatch_set_state(CURRENTLY_RENDERING_FIBER.clone(), action) + }) as Box); + + let function = closure.as_ref().unchecked_ref::().clone(); + + // Don't forget to forget the closure or it will be cleaned up when it goes out of scope. + closure.forget(); + + + return vec![ + initial_state.to_owned(), + function.into(), + ]; +} + +fn dispatch_set_state1(fiber: Option>>) { + log!("dispatch_set_state {:?}", fiber) } +fn dispatch_set_state(fiber: Option>>, action: &JsValue) { + log!("dispatch_set_state {:?}", action) +} + + +// pub fn update_current_dispatcher() { +// unsafe { +// let use_state = || { +// log!("use_state"); +// vec![JsValue::null(), JsValue::null()] +// }; +// let use_callback = || { +// log!("use_callback"); +// }; +// let b = Box::new(Dispatcher::new(&use_state, &use_callback)); +// CURRENT_DISPATCHER.current = Some(b); +// } +// } + +#[wasm_bindgen(js_name = useStateImpl)] +pub unsafe fn use_state(initial_state: &JsValue) -> Vec { + let dispatcher = CURRENT_DISPATCHER.current.as_ref(); + if dispatcher.is_none() { + log!("dispatcher doesn't exist") + } + let use_state = dispatcher.unwrap().use_state; + (*use_state)(initial_state) +} diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index 624d026..ff137d9 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -4,16 +4,13 @@ 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; use crate::work_tags::WorkTag; pub mod fiber; -mod fiber_flags; +pub mod fiber_flags; mod work_tags; mod update_queue; mod work_loop; @@ -22,6 +19,7 @@ mod child_fiber; mod complete_work; mod commit_work; mod fiber_hooks; +mod current_dispatcher; pub trait HostConfig { fn create_text_instance(&self, content: String) -> Rc; @@ -34,6 +32,7 @@ pub struct Reconciler { host_config: Rc, } + impl Reconciler { pub fn new(host_config: Rc) -> Self { Reconciler { host_config } @@ -48,31 +47,12 @@ impl Reconciler { } pub fn update_container(&self, element: Rc, root: Rc>) { - self.update_current_dispatcher(); - let host_root_fiber = Rc::clone(&root).borrow().current.clone(); let update = create_update(element); enqueue_update(host_root_fiber.borrow(), update); 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); - let dispatcher = CURRENT_DISPATCHER.current.clone(); - - log!("dispatcher doesn't exist, {:?}", dispatcher); - } - } } diff --git a/packages/react-reconciler/src/update_queue.rs b/packages/react-reconciler/src/update_queue.rs index 8dfc5ea..4e1b8c4 100644 --- a/packages/react-reconciler/src/update_queue.rs +++ b/packages/react-reconciler/src/update_queue.rs @@ -6,12 +6,11 @@ use web_sys::js_sys::Function; use shared::log; -use crate::fiber::FiberNode; +use crate::fiber::{FiberNode, MemoizedState}; #[derive(Clone, Debug)] pub struct UpdateAction; - #[derive(Clone, Debug)] pub struct Update { pub action: Option>, @@ -22,15 +21,15 @@ pub struct UpdateType { pub pending: Option, } - #[derive(Clone, Debug)] pub struct UpdateQueue { pub shared: UpdateType, } - pub fn create_update(action: Rc) -> Update { - Update { action: Some(action) } + Update { + action: Some(action), + } } pub fn enqueue_update(fiber: Ref, update: Update) { @@ -60,8 +59,10 @@ pub fn process_update_queue(fiber: Rc>) { Some(action) => { let f = action.dyn_ref::(); new_state = match f { - None => Some(action.clone()), - Some(f) => Some(Rc::new(f.call0(&JsValue::null()).unwrap())), + None => Some(MemoizedState::JsValue(action.clone())), + Some(f) => Some(MemoizedState::JsValue(Rc::new( + f.call0(&JsValue::null()).unwrap(), + ))), } } } @@ -70,4 +71,4 @@ pub fn process_update_queue(fiber: Rc>) { } fiber.memoized_state = new_state -} \ No newline at end of file +} diff --git a/packages/react/src/lib.rs b/packages/react/src/lib.rs index f1a9a2e..a9760ff 100644 --- a/packages/react/src/lib.rs +++ b/packages/react/src/lib.rs @@ -1,24 +1,8 @@ -use std::rc::Rc; - use js_sys::{Object, Reflect}; use wasm_bindgen::prelude::*; use shared::REACT_ELEMENT_TYPE; -use crate::current_dispatcher::{Dispatcher, resolve_dispatcher}; - -pub mod current_dispatcher; - -struct MyStruct { - current: Option>, -} - -impl MyStruct { - pub fn new(current: Option>) -> Self { - MyStruct { current } - } -} - #[wasm_bindgen(js_name = jsxDEV)] pub unsafe fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { let react_element = Object::new(); @@ -52,9 +36,14 @@ pub unsafe fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsVal } +#[wasm_bindgen] +extern "C" { + fn useStateImpl() -> Vec; +} + + #[wasm_bindgen(js_name = useState)] pub unsafe fn use_state() -> Vec { - let dispatcher = resolve_dispatcher(); - (&*dispatcher.as_ref().use_state)() + useStateImpl() } From aab52d62876384e677f64a3f0a0a1df295db6aa9 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Sat, 20 Apr 2024 15:41:00 +0800 Subject: [PATCH 3/7] blog-8 make react-dom depend on react --- packages/react-reconciler/src/fiber_hooks.rs | 19 +++++++- packages/react/src/current_dispatcher.rs | 46 ++++++++++++++++++++ packages/react/src/lib.rs | 18 +++++--- 3 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 packages/react/src/current_dispatcher.rs diff --git a/packages/react-reconciler/src/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs index c6d0f14..1bb2041 100644 --- a/packages/react-reconciler/src/fiber_hooks.rs +++ b/packages/react-reconciler/src/fiber_hooks.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use wasm_bindgen::{JsCast, JsValue}; use wasm_bindgen::prelude::{Closure, wasm_bindgen}; -use web_sys::js_sys::Function; +use web_sys::js_sys::{Function, Object, Reflect}; use shared::log; @@ -37,6 +37,12 @@ impl Hook { static mut CURRENTLY_RENDERING_FIBER: Option>> = None; static mut WORK_IN_PROGRESS_HOOK: Option>> = None; + +#[wasm_bindgen] +extern "C" { + fn updateDispatch(args: &JsValue); +} + pub fn render_with_hooks(work_in_progress: Rc>) -> Result { unsafe { CURRENTLY_RENDERING_FIBER = Some(work_in_progress.clone()); } @@ -56,7 +62,15 @@ pub fn render_with_hooks(work_in_progress: Rc>) -> Result Vec>); + + let function = closure.as_ref().unchecked_ref::().clone(); + + // Don't forget to forget the closure or it will be cleaned up when it goes out of scope. + closure.forget(); + Reflect::set(&object, &"use_state".into(), &function); + updateDispatch(&object.into()); } } @@ -129,6 +143,7 @@ fn mount_state(initial_state: &JsValue) -> Vec { ]; } + fn dispatch_set_state1(fiber: Option>>) { log!("dispatch_set_state {:?}", fiber) } diff --git a/packages/react/src/current_dispatcher.rs b/packages/react/src/current_dispatcher.rs new file mode 100644 index 0000000..4ce9879 --- /dev/null +++ b/packages/react/src/current_dispatcher.rs @@ -0,0 +1,46 @@ +use std::any::Any; + +use js_sys::{Function, Reflect}; +use wasm_bindgen::JsValue; +use wasm_bindgen::prelude::*; + +enum UseStateParams { + Function(Box Box>), + Value(Box), +} + +#[derive(Debug)] +pub struct Dispatcher { + pub use_state: Function, //*const dyn Fn(&JsValue) -> Vec, + // pub use_callback: *const dyn Fn(), +} + +unsafe impl Send for Dispatcher {} + +impl Dispatcher { + pub fn new(use_state: Function/*, 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 }; + +fn derive_function_from_js_value(js_value: &JsValue, name: &str) -> Function { + Reflect::get(js_value, &name.into()).unwrap().dyn_into::().unwrap() +} + +#[wasm_bindgen(js_name = updateDispatch)] +pub unsafe fn update_dispatch(args: &JsValue) { + let use_state = derive_function_from_js_value(args, "use_state"); + CURRENT_DISPATCHER.current = Some(Box::new(Dispatcher::new(use_state))) +} + + + diff --git a/packages/react/src/lib.rs b/packages/react/src/lib.rs index a9760ff..a5444d7 100644 --- a/packages/react/src/lib.rs +++ b/packages/react/src/lib.rs @@ -3,6 +3,10 @@ use wasm_bindgen::prelude::*; use shared::REACT_ELEMENT_TYPE; +use crate::current_dispatcher::CURRENT_DISPATCHER; + +pub mod current_dispatcher; + #[wasm_bindgen(js_name = jsxDEV)] pub unsafe fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue { let react_element = Object::new(); @@ -36,14 +40,16 @@ pub unsafe fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsVal } -#[wasm_bindgen] -extern "C" { - fn useStateImpl() -> Vec; -} +// #[wasm_bindgen] +// extern "C" { +// fn useStateImpl() -> Vec; +// } #[wasm_bindgen(js_name = useState)] -pub unsafe fn use_state() -> Vec { - useStateImpl() +pub unsafe fn use_state(initial_state: &JsValue) -> JsValue { + // useStateImpl() + let use_state = &CURRENT_DISPATCHER.current.as_ref().unwrap().use_state; + use_state.call1(&JsValue::null(), initial_state).unwrap() } From 69cfdcb5264f1d8f0c67af4260ff68b7a37c31d3 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Sun, 21 Apr 2024 12:48:54 +0800 Subject: [PATCH 4/7] blog-8 temp commit --- packages/react-reconciler/src/child_fiber.rs | 1 - .../src/current_dispatcher.rs | 34 ---- packages/react-reconciler/src/fiber.rs | 37 ++--- packages/react-reconciler/src/fiber_hooks.rs | 156 +++++++----------- packages/react-reconciler/src/lib.rs | 7 +- packages/react-reconciler/src/update_queue.rs | 18 +- packages/react-reconciler/src/work_loop.rs | 2 +- packages/react/src/current_dispatcher.rs | 13 +- packages/react/src/lib.rs | 7 - scripts/build.js | 12 +- 10 files changed, 105 insertions(+), 182 deletions(-) delete mode 100644 packages/react-reconciler/src/current_dispatcher.rs diff --git a/packages/react-reconciler/src/child_fiber.rs b/packages/react-reconciler/src/child_fiber.rs index 63f220c..47b4ea6 100644 --- a/packages/react-reconciler/src/child_fiber.rs +++ b/packages/react-reconciler/src/child_fiber.rs @@ -81,7 +81,6 @@ fn _reconcile_child_fibers( } } todo!("Unsupported child type when reconcile"); - return None; } pub fn reconcile_child_fibers( diff --git a/packages/react-reconciler/src/current_dispatcher.rs b/packages/react-reconciler/src/current_dispatcher.rs deleted file mode 100644 index 9864def..0000000 --- a/packages/react-reconciler/src/current_dispatcher.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::any::Any; - -use wasm_bindgen::JsValue; - -enum UseStateParams { - Function(Box Box>), - Value(Box), -} - -#[derive(Debug)] -pub struct Dispatcher { - pub use_state: *const dyn Fn(&JsValue) -> Vec, - pub use_callback: *const dyn Fn(), -} - -unsafe impl Send for Dispatcher {} - -impl Dispatcher { - pub fn new(use_state: *const dyn Fn(&JsValue) -> 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 }; - - - diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index e72dacc..7e97fc2 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -12,7 +12,7 @@ use shared::derive_from_js_value; use crate::fiber_flags::Flags; use crate::fiber_hooks::Hook; -use crate::update_queue::{Update, UpdateQueue, UpdateType}; +use crate::update_queue::{Update, UpdateQueue}; use crate::work_tags::WorkTag; #[derive(Debug)] @@ -83,7 +83,7 @@ impl FiberNode { } pub fn enqueue_update(&mut self, update: Update) { - let mut update_queue = match &self.update_queue { + let update_queue = match &self.update_queue { None => { return; } @@ -94,14 +94,6 @@ impl FiberNode { u.shared.pending = Some(update); } - pub fn initialize_update_queue(&mut self) { - self.update_queue = Some(Rc::new(RefCell::new(UpdateQueue { - shared: UpdateType { - pending: Some(Update { action: None }), - }, - }))); - } - pub fn create_work_in_progress( current: Rc>, pending_props: Rc, @@ -176,7 +168,7 @@ impl FiberRootNode { impl Debug for FiberRootNode { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut root = self.current.clone().borrow().alternate.clone(); + let root = self.current.clone().borrow().alternate.clone(); Ok(if let Some(node) = root { let mut queue = VecDeque::new(); queue.push_back(Rc::clone(&node)); @@ -193,7 +185,8 @@ impl Debug for FiberRootNode { current_borrowed._type.as_ref().unwrap(), current_borrowed.flags, current_borrowed.subtree_flags - ); + ) + .expect("print error"); } WorkTag::HostRoot => { write!( @@ -201,7 +194,8 @@ impl Debug for FiberRootNode { "{:?}(subtreeFlags:{:?})", WorkTag::HostRoot, current_ref.subtree_flags - ); + ) + .expect("print error"); } WorkTag::HostComponent => { let current_borrowed = current.borrow(); @@ -216,7 +210,8 @@ impl Debug for FiberRootNode { .unwrap(), current_borrowed.flags, current_borrowed.subtree_flags - ); + ) + .expect("print error"); } WorkTag::HostText => { let current_borrowed = current.borrow(); @@ -233,7 +228,8 @@ impl Debug for FiberRootNode { .as_string() .unwrap(), current_borrowed.flags - ); + ) + .expect("print error"); } }; if let Some(ref child) = current_ref.child { @@ -251,17 +247,18 @@ impl Debug for FiberRootNode { (current_ref._return.as_ref(), next_ref._return.as_ref()) { if !Rc::ptr_eq(current_parent, next_parent) { - writeln!(f, ""); - writeln!(f, "------------------------------------"); + writeln!(f, "").expect("print error"); + writeln!(f, "------------------------------------") + .expect("print error"); continue; } } if current_ref._return.is_some() { - write!(f, ","); + write!(f, ",").expect("print error"); } else { - writeln!(f, ""); - writeln!(f, "------------------------------------"); + writeln!(f, "").expect("print error"); + writeln!(f, "------------------------------------").expect("print error"); } } } diff --git a/packages/react-reconciler/src/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs index 1bb2041..64239f4 100644 --- a/packages/react-reconciler/src/fiber_hooks.rs +++ b/packages/react-reconciler/src/fiber_hooks.rs @@ -7,19 +7,20 @@ use web_sys::js_sys::{Function, Object, Reflect}; use shared::log; -use crate::current_dispatcher::{CURRENT_DISPATCHER, Dispatcher}; use crate::fiber::{FiberNode, MemoizedState}; -use crate::update_queue::UpdateQueue; - -// -// use wasm_bindgen::JsValue; -// -// use crate::fiber::FiberNode; -// use crate::update_queue::UpdateQueue; -// +use crate::update_queue::{create_update, create_update_queue, enqueue_update, UpdateQueue}; + +#[wasm_bindgen] +extern "C" { + fn updateDispatcher(args: &JsValue); +} + +static mut CURRENTLY_RENDERING_FIBER: Option>> = None; +static mut WORK_IN_PROGRESS_HOOK: Option>> = None; + #[derive(Debug, Clone)] pub struct Hook { - memoized_state: Option>, + memoized_state: Option, update_queue: Option>>, next: Option>>, } @@ -34,17 +35,21 @@ impl Hook { } } -static mut CURRENTLY_RENDERING_FIBER: Option>> = None; -static mut WORK_IN_PROGRESS_HOOK: Option>> = None; +fn update_mount_hooks_to_dispatcher() { + let object = Object::new(); + let closure = Closure::wrap(Box::new(mount_state) as Box Vec>); + let function = closure.as_ref().unchecked_ref::().clone(); + closure.forget(); + Reflect::set(&object, &"use_state".into(), &function).expect("TODO: panic set use_state"); -#[wasm_bindgen] -extern "C" { - fn updateDispatch(args: &JsValue); + updateDispatcher(&object.into()); } pub fn render_with_hooks(work_in_progress: Rc>) -> Result { - unsafe { CURRENTLY_RENDERING_FIBER = Some(work_in_progress.clone()); } + unsafe { + CURRENTLY_RENDERING_FIBER = Some(work_in_progress.clone()); + } let work_in_progress_cloned = work_in_progress.clone(); { @@ -52,26 +57,11 @@ pub fn render_with_hooks(work_in_progress: Rc>) -> Result Vec>); - - let function = closure.as_ref().unchecked_ref::().clone(); - - // Don't forget to forget the closure or it will be cleaned up when it goes out of scope. - closure.forget(); - Reflect::set(&object, &"use_state".into(), &function); - updateDispatch(&object.into()); - } + update_mount_hooks_to_dispatcher(); } let _type; @@ -94,11 +84,21 @@ fn mount_work_in_progress_nook() -> Option>> { if CURRENTLY_RENDERING_FIBER.is_none() { log!("WORK_IN_PROGRESS_HOOK and CURRENTLY_RENDERING_FIBER is empty") } else { - CURRENTLY_RENDERING_FIBER.as_ref().unwrap().clone().borrow_mut().memoized_state = Some(MemoizedState::Hook(hook.clone())); + CURRENTLY_RENDERING_FIBER + .as_ref() + .unwrap() + .clone() + .borrow_mut() + .memoized_state = Some(MemoizedState::Hook(hook.clone())); WORK_IN_PROGRESS_HOOK = Some(hook.clone()); } } else { - WORK_IN_PROGRESS_HOOK.as_ref().unwrap().clone().borrow_mut().next = Some(hook.clone()); + WORK_IN_PROGRESS_HOOK + .as_ref() + .unwrap() + .clone() + .borrow_mut() + .next = Some(hook.clone()); WORK_IN_PROGRESS_HOOK = Some(hook.clone()); } WORK_IN_PROGRESS_HOOK.clone() @@ -107,72 +107,40 @@ fn mount_work_in_progress_nook() -> Option>> { fn mount_state(initial_state: &JsValue) -> Vec { let hook = mount_work_in_progress_nook(); - // let memoizedState: State; - // if (initialState instanceof Function) { - // memoizedState = initialState(); - // } else { - // memoizedState = initialState; - // } - // hook.memoizedState = memoizedState; - // - // if (currentlyRenderingFiber === null) { - // console.error('mountState时currentlyRenderingFiber不存在'); - // } - // const queue = createUpdateQueue(); - // hook.updateQueue = queue; - - // let closure = Closure::wrap(Box::new(|| unsafe { - // // dispatch_set_state1(CURRENTLY_RENDERING_FIBER.clone()); - // log!("closure") - // })); + let memoized_state: JsValue; + if initial_state.is_function() { + memoized_state = initial_state + .dyn_ref::() + .unwrap() + .call0(&JsValue::null()) + .unwrap(); + } else { + memoized_state = initial_state.clone(); + } + hook.as_ref().unwrap().clone().borrow_mut().memoized_state = + Some(MemoizedState::JsValue(Rc::new((memoized_state)))); + + unsafe { + if CURRENTLY_RENDERING_FIBER.is_none() { + log!("mount_state, currentlyRenderingFiber is empty"); + } + } + let queue = create_update_queue(); + hook.as_ref().unwrap().clone().borrow_mut().update_queue = Option::from(queue); let closure = Closure::wrap(Box::new(move |action: &JsValue| unsafe { - // web_sys::console::log_1(&"Hello, world!".into()); - dispatch_set_state(CURRENTLY_RENDERING_FIBER.clone(), action) + dispatch_set_state(CURRENTLY_RENDERING_FIBER.clone(), queue.clone(), action) }) as Box); - let function = closure.as_ref().unchecked_ref::().clone(); - - // Don't forget to forget the closure or it will be cleaned up when it goes out of scope. closure.forget(); - - return vec![ - initial_state.to_owned(), - function.into(), - ]; -} - - -fn dispatch_set_state1(fiber: Option>>) { - log!("dispatch_set_state {:?}", fiber) + return vec![initial_state.to_owned(), function.into()]; } -fn dispatch_set_state(fiber: Option>>, action: &JsValue) { - log!("dispatch_set_state {:?}", action) -} - - -// pub fn update_current_dispatcher() { -// unsafe { -// let use_state = || { -// log!("use_state"); -// vec![JsValue::null(), JsValue::null()] -// }; -// let use_callback = || { -// log!("use_callback"); -// }; -// let b = Box::new(Dispatcher::new(&use_state, &use_callback)); -// CURRENT_DISPATCHER.current = Some(b); -// } -// } - -#[wasm_bindgen(js_name = useStateImpl)] -pub unsafe fn use_state(initial_state: &JsValue) -> Vec { - let dispatcher = CURRENT_DISPATCHER.current.as_ref(); - if dispatcher.is_none() { - log!("dispatcher doesn't exist") - } - let use_state = dispatcher.unwrap().use_state; - (*use_state)(initial_state) +fn dispatch_set_state(fiber: Option>>, update_queue: Rc>, action: &JsValue) { + let update = create_update(Rc::new(*action.clone())); + enqueue_update(update_queue, update); + let a = fiber.as_ref().unwrap().borrow(); + // a.s + // scheduleUpdateOnFiber(fiber); } diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index ff137d9..c3ab5d6 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use wasm_bindgen::JsValue; use crate::fiber::{FiberNode, FiberRootNode, StateNode}; -use crate::update_queue::{create_update, enqueue_update}; +use crate::update_queue::{create_update, create_update_queue, enqueue_update}; use crate::work_loop::WorkLoop; use crate::work_tags::WorkTag; @@ -19,7 +19,6 @@ mod child_fiber; mod complete_work; mod commit_work; mod fiber_hooks; -mod current_dispatcher; pub trait HostConfig { fn create_text_instance(&self, content: String) -> Rc; @@ -39,7 +38,7 @@ impl Reconciler { } 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(); + host_root_fiber.clone().borrow_mut().update_queue = Some(create_update_queue()); 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))); @@ -49,7 +48,7 @@ impl Reconciler { pub fn update_container(&self, element: Rc, root: Rc>) { let host_root_fiber = Rc::clone(&root).borrow().current.clone(); let update = create_update(element); - enqueue_update(host_root_fiber.borrow(), update); + enqueue_update(host_root_fiber.borrow().update_queue.clone().unwrap(), update); let mut work_loop = WorkLoop::new(self.host_config.clone()); work_loop.schedule_update_on_fiber(host_root_fiber); } diff --git a/packages/react-reconciler/src/update_queue.rs b/packages/react-reconciler/src/update_queue.rs index 4e1b8c4..08f7eb8 100644 --- a/packages/react-reconciler/src/update_queue.rs +++ b/packages/react-reconciler/src/update_queue.rs @@ -1,4 +1,4 @@ -use std::cell::{Ref, RefCell}; +use std::cell::RefCell; use std::rc::Rc; use wasm_bindgen::{JsCast, JsValue}; @@ -32,12 +32,16 @@ pub fn create_update(action: Rc) -> Update { } } -pub fn enqueue_update(fiber: Ref, update: Update) { - if fiber.update_queue.is_some() { - let uq = fiber.update_queue.clone().unwrap(); - let mut update_queue = uq.borrow_mut(); - update_queue.shared.pending = Some(update); - } +pub fn enqueue_update(update_queue: Rc>, update: Update) { + update_queue.borrow_mut().shared.pending = Option::from(update); +} + +pub fn create_update_queue() -> Rc> { + Rc::new(RefCell::new(UpdateQueue { + shared: UpdateType { + pending: None, + }, + })) } pub fn process_update_queue(fiber: Rc>) { diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 6188116..4714dd2 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -116,7 +116,7 @@ impl WorkLoop { 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()); + let 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(); diff --git a/packages/react/src/current_dispatcher.rs b/packages/react/src/current_dispatcher.rs index 4ce9879..d29414c 100644 --- a/packages/react/src/current_dispatcher.rs +++ b/packages/react/src/current_dispatcher.rs @@ -1,17 +1,10 @@ -use std::any::Any; - use js_sys::{Function, Reflect}; use wasm_bindgen::JsValue; use wasm_bindgen::prelude::*; -enum UseStateParams { - Function(Box Box>), - Value(Box), -} - #[derive(Debug)] pub struct Dispatcher { - pub use_state: Function, //*const dyn Fn(&JsValue) -> Vec, + pub use_state: Function, // pub use_callback: *const dyn Fn(), } @@ -36,8 +29,8 @@ fn derive_function_from_js_value(js_value: &JsValue, name: &str) -> Function { Reflect::get(js_value, &name.into()).unwrap().dyn_into::().unwrap() } -#[wasm_bindgen(js_name = updateDispatch)] -pub unsafe fn update_dispatch(args: &JsValue) { +#[wasm_bindgen(js_name = updateDispatcher)] +pub unsafe fn update_dispatcher(args: &JsValue) { let use_state = derive_function_from_js_value(args, "use_state"); CURRENT_DISPATCHER.current = Some(Box::new(Dispatcher::new(use_state))) } diff --git a/packages/react/src/lib.rs b/packages/react/src/lib.rs index a5444d7..0b91e44 100644 --- a/packages/react/src/lib.rs +++ b/packages/react/src/lib.rs @@ -40,15 +40,8 @@ pub unsafe fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsVal } -// #[wasm_bindgen] -// extern "C" { -// fn useStateImpl() -> Vec; -// } - - #[wasm_bindgen(js_name = useState)] pub unsafe fn use_state(initial_state: &JsValue) -> JsValue { - // useStateImpl() let use_state = &CURRENT_DISPATCHER.current.as_ref().unwrap().use_state; use_state.call1(&JsValue::null(), initial_state).unwrap() } diff --git a/scripts/build.js b/scripts/build.js index 0e8abb2..3cf780f 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -1,6 +1,5 @@ const {execSync} = require('child_process') const fs = require('fs') -const path = require('path') const cwd = process.cwd() @@ -8,10 +7,15 @@ execSync(`wasm-pack build packages/react --out-dir ${cwd}/dist/react --out-name 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`) - +// modify react/package.json 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)) + +// modify react-dom/index_bg.js +const reactDomIndexBgFilename = `${cwd}/dist/react-dom/index_bg.js` +const reactDomIndexBgData = fs.readFileSync(reactDomIndexBgFilename) +fs.writeFileSync(reactDomIndexBgFilename, 'import {updateDispatcher} from "react"\n' + reactDomIndexBgData) + -fs.writeFileSync(packageJsonFilename, JSON.stringify(packageJson)) \ No newline at end of file From 692e600237371dcd4cd77ecedc1892c3ce0b17f0 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Sun, 21 Apr 2024 14:04:52 +0800 Subject: [PATCH 5/7] blog-8 support useState on mount --- examples/hello-world/src/App.tsx | 7 +- packages/react-reconciler/src/fiber.rs | 2 +- packages/react-reconciler/src/fiber_hooks.rs | 29 +++++-- packages/react-reconciler/src/lib.rs | 36 +++++---- packages/react-reconciler/src/update_queue.rs | 7 +- packages/react-reconciler/src/work_loop.rs | 78 ++++++++++++++----- 6 files changed, 110 insertions(+), 49 deletions(-) diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index 2797ddf..ab31453 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1,11 +1,12 @@ -import dayjs from 'dayjs' import {useState} from 'react' function App() { const [a, setA] = useState("a") - console.log(a, setA('ayou')) + setTimeout(() => { + setA('2') + }, 1000) return ( -
{dayjs().format()}
+
{a}
) } diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index 7e97fc2..10d640c 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -130,7 +130,7 @@ impl FiberNode { wip.pending_props = Some(pending_props.clone()); wip.update_queue = Some(c.update_queue.as_ref().unwrap().clone()); wip.flags = c.flags.clone(); - wip.child = Some(Rc::clone(c.child.as_ref().unwrap())); + wip.child = c.child.clone(); wip.memoized_props = c.memoized_props.clone(); wip.memoized_state = c.memoized_state.clone(); w.clone().unwrap() diff --git a/packages/react-reconciler/src/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs index 64239f4..79fff60 100644 --- a/packages/react-reconciler/src/fiber_hooks.rs +++ b/packages/react-reconciler/src/fiber_hooks.rs @@ -9,6 +9,7 @@ use shared::log; use crate::fiber::{FiberNode, MemoizedState}; use crate::update_queue::{create_update, create_update_queue, enqueue_update, UpdateQueue}; +use crate::work_loop::WorkLoop; #[wasm_bindgen] extern "C" { @@ -17,6 +18,7 @@ extern "C" { static mut CURRENTLY_RENDERING_FIBER: Option>> = None; static mut WORK_IN_PROGRESS_HOOK: Option>> = None; +pub static mut WORK_LOOP: Option>> = None; #[derive(Debug, Clone)] pub struct Hook { @@ -126,10 +128,14 @@ fn mount_state(initial_state: &JsValue) -> Vec { } } let queue = create_update_queue(); - hook.as_ref().unwrap().clone().borrow_mut().update_queue = Option::from(queue); + hook.as_ref().unwrap().clone().borrow_mut().update_queue = Option::from(queue.clone()); let closure = Closure::wrap(Box::new(move |action: &JsValue| unsafe { - dispatch_set_state(CURRENTLY_RENDERING_FIBER.clone(), queue.clone(), action) + dispatch_set_state( + CURRENTLY_RENDERING_FIBER.clone().unwrap(), + queue.clone(), + action, + ) }) as Box); let function = closure.as_ref().unchecked_ref::().clone(); closure.forget(); @@ -137,10 +143,19 @@ fn mount_state(initial_state: &JsValue) -> Vec { return vec![initial_state.to_owned(), function.into()]; } -fn dispatch_set_state(fiber: Option>>, update_queue: Rc>, action: &JsValue) { - let update = create_update(Rc::new(*action.clone())); +fn dispatch_set_state( + fiber: Rc>, + update_queue: Rc>, + action: &JsValue, +) { + let update = create_update(Rc::new(action.clone())); enqueue_update(update_queue, update); - let a = fiber.as_ref().unwrap().borrow(); - // a.s - // scheduleUpdateOnFiber(fiber); + unsafe { + WORK_LOOP + .as_ref() + .unwrap() + .clone() + .borrow() + .schedule_update_on_fiber(fiber.clone()); + } } diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index c3ab5d6..ae1dc50 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -5,20 +5,21 @@ use std::rc::Rc; use wasm_bindgen::JsValue; use crate::fiber::{FiberNode, FiberRootNode, StateNode}; +use crate::fiber_hooks::WORK_LOOP; use crate::update_queue::{create_update, create_update_queue, enqueue_update}; use crate::work_loop::WorkLoop; use crate::work_tags::WorkTag; -pub mod fiber; -pub mod fiber_flags; -mod work_tags; -mod update_queue; -mod work_loop; mod begin_work; mod child_fiber; -mod complete_work; mod commit_work; +mod complete_work; +pub mod fiber; +pub mod fiber_flags; mod fiber_hooks; +mod update_queue; +mod work_loop; +mod work_tags; pub trait HostConfig { fn create_text_instance(&self, content: String) -> Rc; @@ -31,7 +32,6 @@ pub struct Reconciler { host_config: Rc, } - impl Reconciler { pub fn new(host_config: Rc) -> Self { Reconciler { host_config } @@ -39,7 +39,10 @@ impl Reconciler { 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().update_queue = Some(create_update_queue()); - let root = Rc::new(RefCell::new(FiberRootNode::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() @@ -48,10 +51,17 @@ impl Reconciler { pub fn update_container(&self, element: Rc, root: Rc>) { let host_root_fiber = Rc::clone(&root).borrow().current.clone(); let update = create_update(element); - enqueue_update(host_root_fiber.borrow().update_queue.clone().unwrap(), update); - let mut work_loop = WorkLoop::new(self.host_config.clone()); - work_loop.schedule_update_on_fiber(host_root_fiber); + enqueue_update( + host_root_fiber.borrow().update_queue.clone().unwrap(), + update, + ); + let work_loop = Rc::new(RefCell::new(WorkLoop::new(self.host_config.clone()))); + unsafe { + WORK_LOOP = Some(work_loop.clone()); + } + work_loop + .clone() + .borrow() + .schedule_update_on_fiber(host_root_fiber); } } - - diff --git a/packages/react-reconciler/src/update_queue.rs b/packages/react-reconciler/src/update_queue.rs index 08f7eb8..f398041 100644 --- a/packages/react-reconciler/src/update_queue.rs +++ b/packages/react-reconciler/src/update_queue.rs @@ -46,9 +46,8 @@ pub fn create_update_queue() -> Rc> { pub fn process_update_queue(fiber: Rc>) { let rc_fiber = fiber.clone(); - let mut fiber = rc_fiber.borrow_mut(); - let mut new_state = None; - match fiber.update_queue.clone() { + let mut new_state = rc_fiber.borrow().memoized_state.clone(); + match rc_fiber.borrow().update_queue.clone() { None => { log!("{:?} process_update_queue, update_queue is empty", fiber) } @@ -74,5 +73,5 @@ pub fn process_update_queue(fiber: Rc>) { } } - fiber.memoized_state = new_state + fiber.clone().borrow_mut().memoized_state = new_state } diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 4714dd2..36d5be9 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -13,6 +13,8 @@ use crate::fiber_flags::get_mutation_mask; use crate::HostConfig; use crate::work_tags::WorkTag; +static mut WORK_IN_PROGRESS: Option>> = None; + pub struct WorkLoop { work_in_progress: Option>>, complete_work: CompleteWork, @@ -26,7 +28,7 @@ impl WorkLoop { } } - pub fn schedule_update_on_fiber(&mut self, fiber: Rc>) { + pub fn schedule_update_on_fiber(&self, fiber: Rc>) { let root = self.mark_update_lane_from_fiber_to_root(fiber); if root.is_none() { return; @@ -68,11 +70,11 @@ impl WorkLoop { None } - fn ensure_root_is_scheduled(&mut self, root: Rc>) { + fn ensure_root_is_scheduled(&self, root: Rc>) { self.perform_sync_work_on_root(root); } - fn perform_sync_work_on_root(&mut self, root: Rc>) { + fn perform_sync_work_on_root(&self, root: Rc>) { self.prepare_fresh_stack(Rc::clone(&root)); loop { @@ -80,10 +82,10 @@ impl WorkLoop { Ok(_) => { break; } - Err(e) => { + Err(e) => unsafe { log!("work_loop error {:?}", e); - self.work_in_progress = None; - } + WORK_IN_PROGRESS = None + }, }; } @@ -125,33 +127,55 @@ impl WorkLoop { } } - fn prepare_fresh_stack(&mut self, root: Rc>) { + fn prepare_fresh_stack(&self, root: Rc>) { let root = Rc::clone(&root); - self.work_in_progress = Some(FiberNode::create_work_in_progress( - root.borrow().current.clone(), - Rc::new(JsValue::null()), - )); + // self.work_in_progress = Some(FiberNode::create_work_in_progress( + // root.borrow().current.clone(), + // Rc::new(JsValue::null()), + // )); + unsafe { + WORK_IN_PROGRESS = Some(FiberNode::create_work_in_progress( + root.borrow().current.clone(), + Rc::new(JsValue::null()), + )); + log!( + "prepare_fresh_stack {:?} {:?}", + WORK_IN_PROGRESS.clone().unwrap().clone().borrow()._type, + WORK_IN_PROGRESS + .clone() + .unwrap() + .clone() + .borrow() + .memoized_state + ); + } } - 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())?; + fn work_loop(&self) -> Result<(), JsValue> { + // while self.work_in_progress.is_some() { + // self.perform_unit_of_work(self.work_in_progress.clone().unwrap())?; + // } + unsafe { + while WORK_IN_PROGRESS.is_some() { + self.perform_unit_of_work(WORK_IN_PROGRESS.clone().unwrap())?; + } } Ok(()) } - fn perform_unit_of_work(&mut self, fiber: Rc>) -> Result<(), JsValue> { + fn perform_unit_of_work(&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()); + // self.work_in_progress = Some(next.unwrap()); + unsafe { WORK_IN_PROGRESS = Some(next.unwrap()) } } Ok(()) } - fn complete_unit_of_work(&mut self, fiber: Rc>) { + fn complete_unit_of_work(&self, fiber: Rc>) { let mut node: Option>> = Some(fiber); loop { @@ -160,13 +184,19 @@ impl WorkLoop { .complete_work(node.clone().unwrap().clone()); if next.is_some() { - self.work_in_progress = next.clone(); + // self.work_in_progress = next.clone(); + unsafe { + WORK_IN_PROGRESS = next.clone(); + } return; } let sibling = node.clone().unwrap().clone().borrow().sibling.clone(); if sibling.is_some() { - self.work_in_progress = next.clone(); + // self.work_in_progress = next.clone(); + unsafe { + WORK_IN_PROGRESS = next.clone(); + } return; } @@ -174,11 +204,17 @@ impl WorkLoop { if _return.is_none() { node = None; - self.work_in_progress = None; + // self.work_in_progress = None; + unsafe { + WORK_IN_PROGRESS = None; + } break; } else { node = _return; - self.work_in_progress = node.clone(); + // self.work_in_progress = node.clone(); + unsafe { + WORK_IN_PROGRESS = node.clone(); + } } } } From 10d294d0aa857825dd0795f3fd4e95bfa516dbbd Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Mon, 22 Apr 2024 18:28:18 +0800 Subject: [PATCH 6/7] blog-8 throw error in useState --- examples/hello-world/src/App.tsx | 4 +++- packages/react-reconciler/src/fiber_hooks.rs | 12 ++++++------ packages/react-reconciler/src/work_loop.rs | 5 +++-- packages/react/src/lib.rs | 4 ++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index ab31453..7a610cb 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1,7 +1,9 @@ import {useState} from 'react' function App() { - const [a, setA] = useState("a") + const [a, setA] = useState(() => { + return "a" + }) setTimeout(() => { setA('2') }, 1000) diff --git a/packages/react-reconciler/src/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs index 79fff60..23c9f06 100644 --- a/packages/react-reconciler/src/fiber_hooks.rs +++ b/packages/react-reconciler/src/fiber_hooks.rs @@ -40,7 +40,7 @@ impl Hook { fn update_mount_hooks_to_dispatcher() { let object = Object::new(); - let closure = Closure::wrap(Box::new(mount_state) as Box Vec>); + let closure = Closure::wrap(Box::new(mount_state) as Box Result, JsValue>>); let function = closure.as_ref().unchecked_ref::().clone(); closure.forget(); Reflect::set(&object, &"use_state".into(), &function).expect("TODO: panic set use_state"); @@ -107,20 +107,20 @@ fn mount_work_in_progress_nook() -> Option>> { } } -fn mount_state(initial_state: &JsValue) -> Vec { +fn mount_state(initial_state: &JsValue) -> Result, JsValue> { let hook = mount_work_in_progress_nook(); let memoized_state: JsValue; + if initial_state.is_function() { memoized_state = initial_state .dyn_ref::() .unwrap() - .call0(&JsValue::null()) - .unwrap(); + .call0(&JsValue::null())?; } else { memoized_state = initial_state.clone(); } hook.as_ref().unwrap().clone().borrow_mut().memoized_state = - Some(MemoizedState::JsValue(Rc::new((memoized_state)))); + Some(MemoizedState::JsValue(Rc::new((memoized_state.clone())))); unsafe { if CURRENTLY_RENDERING_FIBER.is_none() { @@ -140,7 +140,7 @@ fn mount_state(initial_state: &JsValue) -> Vec { let function = closure.as_ref().unchecked_ref::().clone(); closure.forget(); - return vec![initial_state.to_owned(), function.into()]; + Ok(vec![memoized_state, function.into()]) } fn dispatch_set_state( diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 36d5be9..59bf6c2 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -16,14 +16,15 @@ use crate::work_tags::WorkTag; static mut WORK_IN_PROGRESS: Option>> = None; pub struct WorkLoop { - work_in_progress: Option>>, + // make the first param to be &self not &mut self + // work_in_progress: Option>>, complete_work: CompleteWork, } impl WorkLoop { pub fn new(host_config: Rc) -> Self { Self { - work_in_progress: None, + // work_in_progress: None, complete_work: CompleteWork::new(host_config), } } diff --git a/packages/react/src/lib.rs b/packages/react/src/lib.rs index 0b91e44..1de88e7 100644 --- a/packages/react/src/lib.rs +++ b/packages/react/src/lib.rs @@ -41,8 +41,8 @@ pub unsafe fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsVal #[wasm_bindgen(js_name = useState)] -pub unsafe fn use_state(initial_state: &JsValue) -> JsValue { +pub unsafe fn use_state(initial_state: &JsValue) -> Result { let use_state = &CURRENT_DISPATCHER.current.as_ref().unwrap().use_state; - use_state.call1(&JsValue::null(), initial_state).unwrap() + use_state.call1(&JsValue::null(), initial_state) } From a9d50e07e9d9aead74ae268fc19c75794aa76837 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Mon, 22 Apr 2024 18:54:47 +0800 Subject: [PATCH 7/7] blog-8 update demo --- examples/hello-world/src/App.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index 7a610cb..3a7c6a3 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1,14 +1,12 @@ import {useState} from 'react' function App() { - const [a, setA] = useState(() => { - return "a" - }) + const [name, setName] = useState(() => 'ayou') setTimeout(() => { - setA('2') + setName('ayouayou') }, 1000) return ( -
{a}
+
{name}
) }