From 7ddccfcfa864d835850aff41606127240bff140e Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Tue, 6 Aug 2024 18:39:50 +0800 Subject: [PATCH 1/6] support suspense --- packages/react-reconciler/src/begin_work.rs | 38 +++++++++++++++++++ packages/react-reconciler/src/fiber.rs | 9 ++++- packages/react-reconciler/src/fiber_flags.rs | 1 + packages/react-reconciler/src/lib.rs | 1 + .../react-reconciler/src/suspense_context.rs | 17 +++++++++ packages/react-reconciler/src/work_tags.rs | 1 + packages/shared/src/lib.rs | 1 + scripts/build.js | 14 +++++++ 8 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 packages/react-reconciler/src/suspense_context.rs diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs index fac3c81..0bec4eb 100644 --- a/packages/react-reconciler/src/begin_work.rs +++ b/packages/react-reconciler/src/begin_work.rs @@ -12,6 +12,7 @@ use crate::fiber_context::{prepare_to_read_context, propagate_context_change, pu use crate::fiber_flags::Flags; use crate::fiber_hooks::{bailout_hook, render_with_hooks}; use crate::fiber_lanes::{include_some_lanes, Lane}; +use crate::suspense_context::push_suspense_handler; use crate::update_queue::{process_update_queue, ReturnOfProcessUpdateQueue}; use crate::work_tags::WorkTag; @@ -121,9 +122,46 @@ pub fn begin_work( render_lane.clone(), )), WorkTag::MemoComponent => update_memo_component(work_in_progress.clone(), render_lane), + WorkTag::SuspenseComponent => todo!(), }; } +fn mount_suspense_fallback_children( + work_in_progress: Rc>, + primary_children: JsValue, + fallback_children: JsValue, +) { + // let primary_child_props +} + +fn update_suspense_component(work_in_progress: Rc>) { + let current = { work_in_progress.borrow().alternate.clone() }; + let next_props = { work_in_progress.borrow().pending_props.clone() }; + + let mut show_fallback = false; + let did_suspend = + (work_in_progress.borrow().flags.clone() & Flags::DidCapture) != Flags::NoFlags; + + if did_suspend { + show_fallback = true; + work_in_progress.borrow_mut().flags -= Flags::DidCapture; + } + + let next_primary_children = derive_from_js_value(&next_props, "children"); + let next_fallback_children = derive_from_js_value(&next_props, "fallback"); + push_suspense_handler(work_in_progress.clone()); + + if current.is_none() { + if show_fallback { + return mount_suspense_fallback_children( + work_in_progress.clone(), + next_primary_children.clone(), + next_fallback_children.clone(), + ); + } + } +} + fn update_memo_component( work_in_progress: Rc>, render_lane: Lane, diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index dcffed3..565ef46 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -9,7 +9,9 @@ use scheduler::Task; use wasm_bindgen::JsValue; use web_sys::js_sys::Reflect; -use shared::{derive_from_js_value, log, type_of, REACT_MEMO_TYPE, REACT_PROVIDER_TYPE}; +use shared::{ + derive_from_js_value, log, type_of, REACT_MEMO_TYPE, REACT_PROVIDER_TYPE, REACT_SUSPENSE_TYPE, +}; use crate::fiber_context::ContextItem; use crate::fiber_flags::Flags; @@ -176,7 +178,10 @@ impl FiberNode { let _ref = derive_from_js_value(ele, "ref"); let mut fiber_tag = WorkTag::FunctionComponent; - if _type.is_string() { + + if _type == REACT_SUSPENSE_TYPE { + fiber_tag = WorkTag::SuspenseComponent + } else if _type.is_string() { fiber_tag = WorkTag::HostComponent } else if type_of(&_type, "object") { let _typeof = derive_from_js_value(&_type, "$$typeof"); diff --git a/packages/react-reconciler/src/fiber_flags.rs b/packages/react-reconciler/src/fiber_flags.rs index 07cc8e6..b309337 100644 --- a/packages/react-reconciler/src/fiber_flags.rs +++ b/packages/react-reconciler/src/fiber_flags.rs @@ -9,6 +9,7 @@ bitflags! { const ChildDeletion = 0b00010000; const PassiveEffect = 0b00100000; const Ref = 0b01000000; + const DidCapture = 0b1000000; const LayoutMask = 0b01000000; // Ref // effect hook const HookHasEffect = 0b00100001; diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index 3b7eb3b..ba75dda 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -22,6 +22,7 @@ mod fiber_flags; mod fiber_hooks; pub mod fiber_lanes; mod hook_effect_tags; +mod suspense_context; mod sync_task_queue; mod update_queue; mod work_loop; diff --git a/packages/react-reconciler/src/suspense_context.rs b/packages/react-reconciler/src/suspense_context.rs new file mode 100644 index 0000000..5ccad44 --- /dev/null +++ b/packages/react-reconciler/src/suspense_context.rs @@ -0,0 +1,17 @@ +use std::{cell::RefCell, rc::Rc}; + +use crate::fiber::FiberNode; + +static mut SUSPENSE_HANDLER_STACK: Vec>> = vec![]; + +pub fn get_suspense_handler() -> Rc> { + unsafe { SUSPENSE_HANDLER_STACK[SUSPENSE_HANDLER_STACK.len() - 1].clone() } +} + +pub fn push_suspense_handler(handler: Rc>) { + unsafe { SUSPENSE_HANDLER_STACK.push(handler) } +} + +pub fn pop_suspense_handler() { + unsafe { SUSPENSE_HANDLER_STACK.pop() }; +} diff --git a/packages/react-reconciler/src/work_tags.rs b/packages/react-reconciler/src/work_tags.rs index cfecddf..74dcd9d 100644 --- a/packages/react-reconciler/src/work_tags.rs +++ b/packages/react-reconciler/src/work_tags.rs @@ -5,5 +5,6 @@ pub enum WorkTag { HostComponent = 5, HostText = 6, ContextProvider = 8, + SuspenseComponent = 13, MemoComponent = 15, } diff --git a/packages/shared/src/lib.rs b/packages/shared/src/lib.rs index e0c65ab..7cc15f7 100644 --- a/packages/shared/src/lib.rs +++ b/packages/shared/src/lib.rs @@ -6,6 +6,7 @@ pub static REACT_ELEMENT_TYPE: &str = "react.element"; pub static REACT_CONTEXT_TYPE: &str = "react.context"; pub static REACT_PROVIDER_TYPE: &str = "react.provider"; pub static REACT_MEMO_TYPE: &str = "react.memo"; +pub static REACT_SUSPENSE_TYPE: &str = "react.suspense"; #[macro_export] macro_rules! log { diff --git a/scripts/build.js b/scripts/build.js index 5d231d0..899e4bf 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -70,3 +70,17 @@ fs.writeFileSync( ? 'const {updateDispatcher} = require("react");\n' : 'import {updateDispatcher} from "react";\n') + reactDomIndexBgData ) + +// add Suspense +const reactIndexFilename = `${cwd}/dist/react/index.js` +const reactIndexData = fs.readFileSync(reactIndexFilename) +fs.writeFileSync( + reactIndexFilename, + reactIndexData + `export const Suspense='react.suspense';\n` +) +const reactTsIndexFilename = `${cwd}/dist/react/index.d.ts` +const reactTsIndexData = fs.readFileSync(reactTsIndexFilename) +fs.writeFileSync( + reactTsIndexFilename, + reactTsIndexData + `export const Suspense: string;\n` +) From 7ba02b96d89f09428757185923e94d5f36ca5d18 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Wed, 21 Aug 2024 14:50:28 +0800 Subject: [PATCH 2/6] begin work for Suspense --- packages/react-reconciler/src/begin_work.rs | 149 +++++++++++++++++++- packages/react-reconciler/src/fiber.rs | 13 ++ packages/react-reconciler/src/work_tags.rs | 1 + scripts/build.js | 18 +-- 4 files changed, 161 insertions(+), 20 deletions(-) diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs index b930e6b..125fe3a 100644 --- a/packages/react-reconciler/src/begin_work.rs +++ b/packages/react-reconciler/src/begin_work.rs @@ -4,7 +4,7 @@ use std::rc::Rc; use wasm_bindgen::{JsCast, JsValue}; use shared::{derive_from_js_value, is_dev, log, shallow_equal}; -use web_sys::js_sys::{Function, Object}; +use web_sys::js_sys::{Function, Object, Reflect}; use crate::child_fiber::{clone_child_fiblers, mount_child_fibers, reconcile_child_fibers}; use crate::fiber::{FiberNode, MemoizedState}; @@ -123,7 +123,8 @@ pub fn begin_work( )), WorkTag::MemoComponent => update_memo_component(work_in_progress.clone(), render_lane), WorkTag::Fragment => Ok(update_fragment(work_in_progress.clone())), - WorkTag::SuspenseComponent => todo!(), + WorkTag::SuspenseComponent => Ok(update_suspense_component(work_in_progress.clone())), + WorkTag::OffscreenComponent => todo!(), }; } @@ -131,11 +132,127 @@ fn mount_suspense_fallback_children( work_in_progress: Rc>, primary_children: JsValue, fallback_children: JsValue, -) { - // let primary_child_props +) -> Rc> { + let primary_child_props = Object::new(); + Reflect::set(&primary_child_props, &"mode".into(), &"hidden".into()); + Reflect::set(&primary_child_props, &"children".into(), &primary_children); + + let primary_child_fragment = Rc::new(RefCell::new(FiberNode::create_fiber_from_offscreen( + primary_child_props.into(), + ))); + let fallback_child_fragment = Rc::new(RefCell::new(FiberNode::create_fiber_from_fragment( + fallback_children, + JsValue::null(), + ))); + + fallback_child_fragment.borrow_mut().flags |= Flags::Placement; + + primary_child_fragment.borrow_mut()._return = Some(work_in_progress.clone()); + fallback_child_fragment.borrow_mut()._return = Some(work_in_progress.clone()); + primary_child_fragment.borrow_mut().sibling = Some(fallback_child_fragment.clone()); + work_in_progress.borrow_mut().child = Some(primary_child_fragment.clone()); + + fallback_child_fragment } -fn update_suspense_component(work_in_progress: Rc>) { +fn update_suspense_fallback_children( + work_in_progress: Rc>, + primary_children: JsValue, + fallback_children: JsValue, +) -> Rc> { + let current = { work_in_progress.borrow().alternate.clone().unwrap() }; + let current_primary_child_fragment = current.borrow().child.clone().unwrap(); + let current_fallback_child_fragment = current_primary_child_fragment.borrow().sibling.clone(); + + let primary_child_props = Object::new(); + Reflect::set(&primary_child_props, &"mode".into(), &"hidden".into()); + Reflect::set(&primary_child_props, &"children".into(), &primary_children); + + let primary_child_fragment = FiberNode::create_work_in_progress( + current_primary_child_fragment, + primary_child_props.into(), + ); + + let mut fallback_child_fragment; + + if current_fallback_child_fragment.is_some() { + fallback_child_fragment = FiberNode::create_work_in_progress( + current_fallback_child_fragment.unwrap(), + fallback_children, + ); + } else { + fallback_child_fragment = Rc::new(RefCell::new(FiberNode::create_fiber_from_fragment( + fallback_children, + JsValue::null(), + ))); + fallback_child_fragment.borrow_mut().flags |= Flags::Placement; + } + + primary_child_fragment.borrow_mut()._return = Some(work_in_progress.clone()); + fallback_child_fragment.borrow_mut()._return = Some(work_in_progress.clone()); + primary_child_fragment.borrow_mut().sibling = Some(fallback_child_fragment.clone()); + work_in_progress.borrow_mut().child = Some(primary_child_fragment.clone()); + + fallback_child_fragment +} + +fn mount_suspense_primary_children( + work_in_progress: Rc>, + primary_children: JsValue, +) -> Rc> { + let primary_child_props = Object::new(); + Reflect::set(&primary_child_props, &"mode".into(), &"visible".into()); + Reflect::set(&primary_child_props, &"children".into(), &primary_children); + + let primary_child_fragment = Rc::new(RefCell::new(FiberNode::create_fiber_from_offscreen( + primary_child_props.into(), + ))); + + primary_child_fragment.borrow_mut()._return = Some(work_in_progress.clone()); + work_in_progress.borrow_mut().child = Some(primary_child_fragment.clone()); + + primary_child_fragment +} + +fn update_suspense_primary_children( + work_in_progress: Rc>, + primary_children: JsValue, +) -> Rc> { + let current = { work_in_progress.borrow().alternate.clone().unwrap() }; + let current_primary_child_fragment = current.borrow().child.clone().unwrap(); + let current_fallback_child_fragment = current_primary_child_fragment.borrow().sibling.clone(); + + let primary_child_props = Object::new(); + Reflect::set(&primary_child_props, &"mode".into(), &"visible".into()); + Reflect::set(&primary_child_props, &"children".into(), &primary_children); + + let primary_child_fragment = FiberNode::create_work_in_progress( + current_primary_child_fragment.clone(), + primary_child_props.into(), + ); + + primary_child_fragment.borrow_mut()._return = Some(work_in_progress.clone()); + primary_child_fragment.borrow_mut().sibling = None; + work_in_progress.borrow_mut().child = Some(primary_child_fragment.clone()); + + if current_fallback_child_fragment.is_some() { + let current_fallback_child_fragment = current_fallback_child_fragment.unwrap(); + let mut deletions = &work_in_progress.borrow().deletions; + if deletions.is_empty() { + work_in_progress.borrow_mut().deletions = vec![current_fallback_child_fragment]; + work_in_progress.borrow_mut().flags != Flags::ChildDeletion; + } else { + let deletions = &mut work_in_progress.borrow_mut().deletions; + deletions.push(current_primary_child_fragment) + } + } + + primary_child_fragment +} + +fn update_suspense_component( + work_in_progress: Rc>, +) -> Option>> { let current = { work_in_progress.borrow().alternate.clone() }; let next_props = { work_in_progress.borrow().pending_props.clone() }; @@ -154,11 +271,29 @@ fn update_suspense_component(work_in_progress: Rc>) { if current.is_none() { if show_fallback { - return mount_suspense_fallback_children( + return Some(mount_suspense_fallback_children( work_in_progress.clone(), next_primary_children.clone(), next_fallback_children.clone(), - ); + )); + } else { + return Some(mount_suspense_primary_children( + work_in_progress.clone(), + next_primary_children.clone(), + )); + } + } else { + if show_fallback { + return Some(update_suspense_fallback_children( + work_in_progress.clone(), + next_primary_children.clone(), + next_fallback_children.clone(), + )); + } else { + return Some(update_suspense_primary_children( + work_in_progress.clone(), + next_primary_children.clone(), + )); } } } diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index 8f08ba7..d6bdfb8 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -144,6 +144,15 @@ impl FiberNode { } } + pub fn create_fiber_from_offscreen(pending_props: JsValue) -> FiberNode { + FiberNode::new( + WorkTag::OffscreenComponent, + pending_props, + JsValue::null(), + JsValue::null(), + ) + } + pub fn create_fiber_from_fragment(elements: JsValue, key: JsValue) -> FiberNode { FiberNode::new(WorkTag::Fragment, elements, key, JsValue::null()) } @@ -281,6 +290,8 @@ pub struct FiberRootNode { pub finished_work: Option>>, pub pending_lanes: Lane, pub finished_lanes: Lane, + pub suspended_lanes: Lane, + pub pinged_lanes: Lane, pub callback_node: Option, pub callback_priority: Lane, pub pending_passive_effects: Rc>, @@ -300,6 +311,8 @@ impl FiberRootNode { })), callback_node: None, callback_priority: Lane::NoLane, + pinged_lanes: Lane::NoLane, + suspended_lanes: Lane::NoLane, } } diff --git a/packages/react-reconciler/src/work_tags.rs b/packages/react-reconciler/src/work_tags.rs index bda1a20..1246990 100644 --- a/packages/react-reconciler/src/work_tags.rs +++ b/packages/react-reconciler/src/work_tags.rs @@ -7,5 +7,6 @@ pub enum WorkTag { Fragment = 7, ContextProvider = 8, SuspenseComponent = 13, + OffscreenComponent = 14, MemoComponent = 15, } diff --git a/scripts/build.js b/scripts/build.js index 9d5cc60..fa31a30 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -71,26 +71,18 @@ fs.writeFileSync( : 'import {updateDispatcher} from "react";\n') + reactDomIndexBgData ) -// add Suspense +// add Suspense + Fragment const reactIndexFilename = `${cwd}/dist/react/index.js` const reactIndexData = fs.readFileSync(reactIndexFilename) fs.writeFileSync( reactIndexFilename, - reactIndexData + `export const Suspense='react.suspense';\n` + reactIndexData + + `export const Suspense='react.suspense';\nexport const Fragment='react.fragment';\n` ) const reactTsIndexFilename = `${cwd}/dist/react/index.d.ts` const reactTsIndexData = fs.readFileSync(reactTsIndexFilename) fs.writeFileSync( reactTsIndexFilename, - reactTsIndexData + `export const Suspense: string;\n` -) - -// add Fragment -fs.writeFileSync( - reactIndexFilename, - reactIndexData + `export const Fragment='react.fragment';\n` -) -fs.writeFileSync( - reactTsIndexFilename, - reactTsIndexData + `export const Fragment: string;\n` + reactTsIndexData + + `export const Suspense: string;\nexport const Fragment: string;\n` ) From 03a4da4b419a622541c667c9f730ad8aa95423d9 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Wed, 21 Aug 2024 14:50:39 +0800 Subject: [PATCH 3/6] refactor workloop --- .../react-reconciler/src/complete_work.rs | 25 +++++++ packages/react-reconciler/src/fiber_flags.rs | 31 +++++--- packages/react-reconciler/src/fiber_lanes.rs | 16 ++++ .../react-reconciler/src/hook_effect_tags.rs | 9 --- packages/react-reconciler/src/lib.rs | 1 - packages/react-reconciler/src/work_loop.rs | 74 ++++++------------- 6 files changed, 81 insertions(+), 75 deletions(-) delete mode 100644 packages/react-reconciler/src/hook_effect_tags.rs diff --git a/packages/react-reconciler/src/complete_work.rs b/packages/react-reconciler/src/complete_work.rs index 6c473aa..cb56386 100644 --- a/packages/react-reconciler/src/complete_work.rs +++ b/packages/react-reconciler/src/complete_work.rs @@ -11,6 +11,7 @@ use crate::fiber::{FiberNode, StateNode}; use crate::fiber_context::pop_provider; use crate::fiber_flags::Flags; use crate::fiber_lanes::{merge_lanes, Lane}; +use crate::suspense_context::pop_suspense_handler; use crate::work_tags::WorkTag; use crate::HostConfig; @@ -212,6 +213,30 @@ impl CompleteWork { self.bubble_properties(work_in_progress.clone()); None } + // WorkTag::SuspenseComponent => { + // pop_suspense_handler(); + // let offscreen_fiber = work_in_progress.borrow().child.clone().unwrap(); + // let is_hidden = + // derive_from_js_value(&offscreen_fiber.borrow().pending_props, "mode") + // .as_string() + // .unwrap() + // == "hidden"; + // let current_offscreen_fiber = offscreen_fiber.borrow().alternate.clone(); + // if current_offscreen_fiber.is_some() { + // let current_offscreen_fiber = current_offscreen_fiber.unwrap(); + // let was_hidden = derive_from_js_value( + // ¤t_offscreen_fiber.borrow().pending_props, + // "mode", + // ) + // .as_string() + // .unwrap() + // == "hidden"; + // if is_hidden != was_hidden { + // offscreen_fiber.borrow_mut().flags != + // } + // } + // None + // } _ => { self.bubble_properties(work_in_progress.clone()); None diff --git a/packages/react-reconciler/src/fiber_flags.rs b/packages/react-reconciler/src/fiber_flags.rs index b309337..06e221f 100644 --- a/packages/react-reconciler/src/fiber_flags.rs +++ b/packages/react-reconciler/src/fiber_flags.rs @@ -2,18 +2,21 @@ use bitflags::bitflags; bitflags! { #[derive(Debug, Clone)] - pub struct Flags: u8 { - const NoFlags = 0b00000000; - const Placement = 0b00000010; - const Update = 0b00000100; - const ChildDeletion = 0b00010000; - const PassiveEffect = 0b00100000; - const Ref = 0b01000000; - const DidCapture = 0b1000000; - const LayoutMask = 0b01000000; // Ref - // effect hook - const HookHasEffect = 0b00100001; - const Passive = 0b00000010; + pub struct Flags: u16 { + const NoFlags = 0b00000000; + const Placement = 0b00000001; + const Update = 0b00000010; + const ChildDeletion = 0b00000100; + const PassiveEffect = 0b00001000; + const Ref = 0b00010000; + const Visibility = 0b00100000; + const DidCapture = 0b01000000; + const ShouldCapture = 0b1000000000000; + + const LayoutMask = 0b00010000; // Ref + // HookEffectTags + const HookHasEffect = 0b0001; + const Passive = 0b0010; // useEffect } } @@ -30,3 +33,7 @@ pub fn get_mutation_mask() -> Flags { pub fn get_passive_mask() -> Flags { Flags::PassiveEffect | Flags::ChildDeletion } + +pub fn get_host_effect_mask() -> Flags { + get_mutation_mask() | Flags::LayoutMask | get_passive_mask() | Flags::DidCapture +} diff --git a/packages/react-reconciler/src/fiber_lanes.rs b/packages/react-reconciler/src/fiber_lanes.rs index 916419f..d9f46be 100644 --- a/packages/react-reconciler/src/fiber_lanes.rs +++ b/packages/react-reconciler/src/fiber_lanes.rs @@ -1,5 +1,10 @@ use bitflags::bitflags; use scheduler::{unstable_get_current_priority_level, Priority}; +use std::cell::RefCell; +use std::pin; +use std::rc::Rc; + +use crate::fiber::FiberRootNode; bitflags! { #[derive(Debug, Clone)] @@ -68,3 +73,14 @@ pub fn include_some_lanes(set: Lane, subset: Lane) -> bool { pub fn remove_lanes(set: Lane, subset: Lane) -> Lane { return set - subset; } + +pub fn mark_root_pinged(root: Rc>, pinged_lane: Lane) { + let suspended_lanes: Lane = { root.borrow().suspended_lanes.clone() }; + root.borrow_mut().pinged_lanes |= suspended_lanes & pinged_lane; +} + +pub fn mark_root_suspended(root: Rc>, suspended_lane: Lane) { + let suspended_lanes = { root.borrow().suspended_lanes.clone() }; + root.borrow_mut().suspended_lanes |= suspended_lane; + root.borrow_mut().pinged_lanes -= suspended_lanes; +} diff --git a/packages/react-reconciler/src/hook_effect_tags.rs b/packages/react-reconciler/src/hook_effect_tags.rs deleted file mode 100644 index f6f5678..0000000 --- a/packages/react-reconciler/src/hook_effect_tags.rs +++ /dev/null @@ -1,9 +0,0 @@ -use bitflags::bitflags; - -bitflags! { - #[derive(Debug, Clone)] - pub struct HookEffectTags: u8 { - const HookHasEffect = 0b0001; - const Passive = 0b0010; // useEffect - } -} diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index 4463768..3df3275 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -21,7 +21,6 @@ mod fiber_context; mod fiber_flags; mod fiber_hooks; pub mod fiber_lanes; -mod hook_effect_tags; mod suspense_context; mod sync_task_queue; mod update_queue; diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 87ce5c9..ebb3b0e 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -1,7 +1,6 @@ use std::cell::RefCell; use std::rc::Rc; -use bitflags::bitflags; use wasm_bindgen::closure::Closure; use wasm_bindgen::{JsCast, JsValue}; use web_sys::js_sys::Function; @@ -19,34 +18,27 @@ use crate::commit_work::{ }; use crate::fiber::{FiberNode, FiberRootNode, PendingPassiveEffects, StateNode}; use crate::fiber_flags::{get_mutation_mask, get_passive_mask, Flags}; -use crate::fiber_lanes::{get_highest_priority, lanes_to_scheduler_priority, merge_lanes, Lane}; +use crate::fiber_lanes::{ + get_highest_priority, lanes_to_scheduler_priority, mark_root_suspended, merge_lanes, Lane, +}; use crate::sync_task_queue::{flush_sync_callbacks, schedule_sync_callback}; use crate::work_tags::WorkTag; use crate::{COMPLETE_WORK, HOST_CONFIG}; -bitflags! { - #[derive(Debug, Clone)] - pub struct ExecutionContext: u8 { - const NoContext = 0b0000; - const RenderContext = 0b0010; - const CommitContext = 0b0100; - const ChildDeletion = 0b00010000; - } -} - -impl PartialEq for ExecutionContext { - fn eq(&self, other: &Self) -> bool { - self.bits() == other.bits() - } -} - static mut WORK_IN_PROGRESS: Option>> = None; static mut WORK_IN_PROGRESS_ROOT_RENDER_LANE: Lane = Lane::NoLane; -static mut EXECUTION_CONTEXT: ExecutionContext = ExecutionContext::NoContext; static mut ROOT_DOES_HAVE_PASSIVE_EFFECTS: bool = false; +static mut WORK_IN_PROGRESS_ROOT_EXIT_STATUS: u8 = ROOT_IN_PROGRESS; +static mut WORK_IN_PROGRESS_SUSPENDED_REASON: u8 = NOT_SUSPENDED; +static mut WORK_IN_PROGRESS_THROWN_VALUE: Option = None; +static ROOT_IN_PROGRESS: u8 = 0; static ROOT_INCOMPLETE: u8 = 1; static ROOT_COMPLETED: u8 = 2; +static ROOT_DID_NOT_COMPLETE: u8 = 3; + +static NOT_SUSPENDED: u8 = 0; +static SUSPENDED_ON_DATA: u8 = 6; pub fn schedule_update_on_fiber(fiber: Rc>, lane: Lane) { if is_dev() { @@ -174,12 +166,6 @@ fn render_root(root: Rc>, lanes: Lane, should_time_slice: ); } - let prev_execution_context: ExecutionContext; - unsafe { - prev_execution_context = EXECUTION_CONTEXT.clone(); - EXECUTION_CONTEXT |= ExecutionContext::RenderContext; - } - prepare_fresh_stack(root.clone(), lanes.clone()); loop { @@ -203,7 +189,6 @@ fn render_root(root: Rc>, lanes: Lane, should_time_slice: // log!("render over"); unsafe { - EXECUTION_CONTEXT = prev_execution_context; WORK_IN_PROGRESS_ROOT_RENDER_LANE = Lane::NoLane; if should_time_slice && WORK_IN_PROGRESS.is_some() { @@ -219,15 +204,6 @@ fn render_root(root: Rc>, lanes: Lane, should_time_slice: } fn perform_concurrent_work_on_root(root: Rc>, did_timeout: bool) -> JsValue { - unsafe { - if EXECUTION_CONTEXT.clone() - & (ExecutionContext::RenderContext | ExecutionContext::CommitContext) - != ExecutionContext::NoContext - { - panic!("No in React work process {:?}", EXECUTION_CONTEXT) - } - } - // 开始执行具体工作前,保证上一次的useEffct都执行了 // 同时要注意useEffect执行时触发的更新优先级是否大于当前更新的优先级 let did_flush_passive_effects = @@ -288,7 +264,7 @@ fn perform_concurrent_work_on_root(root: Rc>, did_timeout } fn perform_sync_work_on_root(root: Rc>, lanes: Lane) { - let next_lane = get_highest_priority(root.borrow().pending_lanes.clone()); + let next_lane = root.borrow().get_next_lanes(); if next_lane != Lane::SyncLane { ensure_root_is_scheduled(root.clone()); @@ -309,8 +285,12 @@ fn perform_sync_work_on_root(root: Rc>, lanes: Lane) { }; root.clone().borrow_mut().finished_work = finished_work; root.clone().borrow_mut().finished_lanes = lanes; - + unsafe { WORK_IN_PROGRESS_ROOT_RENDER_LANE = Lane::NoLane }; commit_root(root); + } else if exit_status == ROOT_DID_NOT_COMPLETE { + unsafe { WORK_IN_PROGRESS_ROOT_RENDER_LANE = Lane::NoLane }; + mark_root_suspended(root.clone(), next_lane); + ensure_root_is_scheduled(root.clone()); } else { todo!("Unsupported status of sync render") } @@ -318,12 +298,6 @@ fn perform_sync_work_on_root(root: Rc>, lanes: Lane) { fn flush_passive_effects(pending_passive_effects: Rc>) -> bool { unsafe { - if EXECUTION_CONTEXT - .contains(ExecutionContext::RenderContext | ExecutionContext::CommitContext) - { - log!("Cannot execute useEffect callback in React work loop") - } - let mut did_flush_passive_effects = false; for effect in &pending_passive_effects.borrow().unmount { did_flush_passive_effects = true; @@ -388,12 +362,6 @@ fn commit_root(root: Rc>) { let root_has_effect = get_mutation_mask().contains(flags); if subtree_has_effect || root_has_effect { - let prev_execution_context: ExecutionContext; - unsafe { - prev_execution_context = EXECUTION_CONTEXT.clone(); - EXECUTION_CONTEXT |= ExecutionContext::CommitContext; - } - // effect // 1/3: Before Mutation @@ -406,10 +374,6 @@ fn commit_root(root: Rc>) { // 3/3: Layout commit_layout_effects(finished_work.clone(), root.clone()); - - unsafe { - EXECUTION_CONTEXT = prev_execution_context; - } } else { cloned.borrow_mut().current = finished_work.clone(); } @@ -428,6 +392,10 @@ fn prepare_fresh_stack(root: Rc>, lane: Lane) { JsValue::null(), )); WORK_IN_PROGRESS_ROOT_RENDER_LANE = lane; + + WORK_IN_PROGRESS_ROOT_EXIT_STATUS = ROOT_IN_PROGRESS; + WORK_IN_PROGRESS_SUSPENDED_REASON = NOT_SUSPENDED; + WORK_IN_PROGRESS_THROWN_VALUE = None; } } From 898b840650b12feb2a23284e2ad09c772778bb31 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Wed, 21 Aug 2024 19:14:30 +0800 Subject: [PATCH 4/6] handle thrown value --- examples/hello-world/src/App.tsx | 2 +- examples/hello-world/src/suspense/index.tsx | 15 ++++ packages/react-reconciler/src/begin_work.rs | 13 ++- packages/react-reconciler/src/fiber.rs | 5 +- packages/react-reconciler/src/fiber_throw.rs | 22 +++++ .../react-reconciler/src/fiber_unwind_work.rs | 34 ++++++++ packages/react-reconciler/src/lib.rs | 3 + .../react-reconciler/src/suspense_context.rs | 13 ++- packages/react-reconciler/src/thenable.rs | 6 ++ packages/react-reconciler/src/work_loop.rs | 80 +++++++++++++++++-- 10 files changed, 176 insertions(+), 17 deletions(-) create mode 100644 examples/hello-world/src/suspense/index.tsx create mode 100644 packages/react-reconciler/src/fiber_throw.rs create mode 100644 packages/react-reconciler/src/fiber_unwind_work.rs create mode 100644 packages/react-reconciler/src/thenable.rs diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index 08e9d72..a8a8fb1 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1 +1 @@ -export {default} from './fragment' +export {default} from './suspense' diff --git a/examples/hello-world/src/suspense/index.tsx b/examples/hello-world/src/suspense/index.tsx new file mode 100644 index 0000000..93d990c --- /dev/null +++ b/examples/hello-world/src/suspense/index.tsx @@ -0,0 +1,15 @@ +import {Suspense} from 'react' + +export default function App() { + return ( + loading}> + + + ) +} + +function Child() { + debugger + throw Promise.resolve(1) + return

Child

+} diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs index 125fe3a..4bad91e 100644 --- a/packages/react-reconciler/src/begin_work.rs +++ b/packages/react-reconciler/src/begin_work.rs @@ -124,7 +124,7 @@ pub fn begin_work( WorkTag::MemoComponent => update_memo_component(work_in_progress.clone(), render_lane), WorkTag::Fragment => Ok(update_fragment(work_in_progress.clone())), WorkTag::SuspenseComponent => Ok(update_suspense_component(work_in_progress.clone())), - WorkTag::OffscreenComponent => todo!(), + WorkTag::OffscreenComponent => Ok(update_offscreen_component(work_in_progress)), }; } @@ -268,7 +268,7 @@ fn update_suspense_component( let next_primary_children = derive_from_js_value(&next_props, "children"); let next_fallback_children = derive_from_js_value(&next_props, "fallback"); push_suspense_handler(work_in_progress.clone()); - + log!("show_fallback {:?}", show_fallback); if current.is_none() { if show_fallback { return Some(mount_suspense_fallback_children( @@ -298,6 +298,15 @@ fn update_suspense_component( } } +fn update_offscreen_component( + work_in_progress: Rc>, +) -> Option>> { + let next_props = work_in_progress.borrow().pending_props.clone(); + let next_children = derive_from_js_value(&next_props, "children"); + reconcile_children(work_in_progress.clone(), Some(next_children)); + work_in_progress.borrow().child.clone() +} + fn update_fragment(work_in_progress: Rc>) -> Option>> { let next_children = work_in_progress.borrow().pending_props.clone(); reconcile_children(work_in_progress.clone(), Some(next_children)); diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index d6bdfb8..b1d4df7 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -83,9 +83,10 @@ impl Debug for FiberNode { WorkTag::HostRoot => { write!( f, - "{:?}(subtreeFlags:{:?})", + "{:?}(subtreeFlags:{:?}),flags:{:?})", WorkTag::HostRoot, - self.subtree_flags + self.subtree_flags, + self.flags ) .expect("print error"); } diff --git a/packages/react-reconciler/src/fiber_throw.rs b/packages/react-reconciler/src/fiber_throw.rs new file mode 100644 index 0000000..7705ed0 --- /dev/null +++ b/packages/react-reconciler/src/fiber_throw.rs @@ -0,0 +1,22 @@ +use std::{cell::RefCell, rc::Rc}; + +use shared::{derive_from_js_value, type_of}; +use wasm_bindgen::JsValue; + +use crate::{ + fiber::FiberRootNode, fiber_flags::Flags, fiber_lanes::Lane, + suspense_context::get_suspense_handler, +}; + +pub fn throw_exception(root: Rc>, value: JsValue, lane: Lane) { + if !value.is_null() + && type_of(&value, "object") + && derive_from_js_value(&value, "then").is_function() + { + let suspense_boundary = get_suspense_handler(); + if suspense_boundary.is_some() { + let suspense_boundary = suspense_boundary.unwrap(); + suspense_boundary.borrow_mut().flags |= Flags::ShouldCapture; + } + } +} diff --git a/packages/react-reconciler/src/fiber_unwind_work.rs b/packages/react-reconciler/src/fiber_unwind_work.rs new file mode 100644 index 0000000..d52b72d --- /dev/null +++ b/packages/react-reconciler/src/fiber_unwind_work.rs @@ -0,0 +1,34 @@ +use std::{cell::RefCell, rc::Rc}; + +use shared::derive_from_js_value; + +use crate::{ + fiber::FiberNode, + fiber_context::pop_provider, + fiber_flags::Flags, + suspense_context::pop_suspense_handler, + work_tags::WorkTag::{ContextProvider, SuspenseComponent}, +}; + +pub fn unwind_work(wip: Rc>) -> Option>> { + let flags = wip.borrow().flags.clone(); + let tag = wip.borrow().tag.clone(); + match tag { + SuspenseComponent => { + pop_suspense_handler(); + if (flags.clone() & Flags::ShouldCapture) != Flags::NoFlags + && (flags.clone() & Flags::DidCapture) == Flags::NoFlags + { + wip.borrow_mut().flags = (flags - Flags::ShouldCapture) | Flags::DidCapture; + return Some(wip.clone()); + } + None + } + ContextProvider => { + let context = derive_from_js_value(&wip.borrow()._type, "_context"); + pop_provider(&context); + None + } + _ => None, + } +} diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs index 3df3275..d63aef5 100644 --- a/packages/react-reconciler/src/lib.rs +++ b/packages/react-reconciler/src/lib.rs @@ -21,8 +21,11 @@ mod fiber_context; mod fiber_flags; mod fiber_hooks; pub mod fiber_lanes; +mod fiber_throw; +mod fiber_unwind_work; mod suspense_context; mod sync_task_queue; +mod thenable; mod update_queue; mod work_loop; pub mod work_tags; diff --git a/packages/react-reconciler/src/suspense_context.rs b/packages/react-reconciler/src/suspense_context.rs index 5ccad44..5f7afe1 100644 --- a/packages/react-reconciler/src/suspense_context.rs +++ b/packages/react-reconciler/src/suspense_context.rs @@ -4,14 +4,19 @@ use crate::fiber::FiberNode; static mut SUSPENSE_HANDLER_STACK: Vec>> = vec![]; -pub fn get_suspense_handler() -> Rc> { - unsafe { SUSPENSE_HANDLER_STACK[SUSPENSE_HANDLER_STACK.len() - 1].clone() } +pub fn get_suspense_handler() -> Option>> { + unsafe { + if SUSPENSE_HANDLER_STACK.len() <= 0 { + return None; + } + return Some(SUSPENSE_HANDLER_STACK[SUSPENSE_HANDLER_STACK.len() - 1].clone()); + } } pub fn push_suspense_handler(handler: Rc>) { unsafe { SUSPENSE_HANDLER_STACK.push(handler) } } -pub fn pop_suspense_handler() { - unsafe { SUSPENSE_HANDLER_STACK.pop() }; +pub fn pop_suspense_handler() -> Option>> { + unsafe { SUSPENSE_HANDLER_STACK.pop() } } diff --git a/packages/react-reconciler/src/thenable.rs b/packages/react-reconciler/src/thenable.rs new file mode 100644 index 0000000..fbfdd14 --- /dev/null +++ b/packages/react-reconciler/src/thenable.rs @@ -0,0 +1,6 @@ +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +extern "C" { + type SuspenseException; +} diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index ebb3b0e..191c5bb 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -17,10 +17,12 @@ use crate::commit_work::{ commit_hook_effect_list_unmount, commit_layout_effects, commit_mutation_effects, }; use crate::fiber::{FiberNode, FiberRootNode, PendingPassiveEffects, StateNode}; -use crate::fiber_flags::{get_mutation_mask, get_passive_mask, Flags}; +use crate::fiber_flags::{get_host_effect_mask, get_mutation_mask, get_passive_mask, Flags}; use crate::fiber_lanes::{ get_highest_priority, lanes_to_scheduler_priority, mark_root_suspended, merge_lanes, Lane, }; +use crate::fiber_throw::throw_exception; +use crate::fiber_unwind_work::unwind_work; use crate::sync_task_queue::{flush_sync_callbacks, schedule_sync_callback}; use crate::work_tags::WorkTag; use crate::{COMPLETE_WORK, HOST_CONFIG}; @@ -154,7 +156,7 @@ fn ensure_root_is_scheduled(root: Rc>) { root.borrow_mut().callback_priority = cur_priority; } -fn render_root(root: Rc>, lanes: Lane, should_time_slice: bool) -> u8 { +fn render_root(root: Rc>, lane: Lane, should_time_slice: bool) -> u8 { if is_dev() { log!( "Start {:?} render", @@ -166,9 +168,26 @@ fn render_root(root: Rc>, lanes: Lane, should_time_slice: ); } - prepare_fresh_stack(root.clone(), lanes.clone()); + if unsafe { WORK_IN_PROGRESS_ROOT_RENDER_LANE != lane } { + prepare_fresh_stack(root.clone(), lane.clone()); + } loop { + unsafe { + if WORK_IN_PROGRESS_SUSPENDED_REASON != NOT_SUSPENDED && WORK_IN_PROGRESS.is_some() { + let thrown_value = WORK_IN_PROGRESS_THROWN_VALUE.clone().unwrap(); + + WORK_IN_PROGRESS_SUSPENDED_REASON = NOT_SUSPENDED; + WORK_IN_PROGRESS_THROWN_VALUE = None; + + throw_and_unwind_work_loop( + root.clone(), + WORK_IN_PROGRESS.clone().unwrap(), + thrown_value, + lane.clone(), + ); + } + } match if should_time_slice { work_loop_concurrent() } else { @@ -177,10 +196,7 @@ fn render_root(root: Rc>, lanes: Lane, should_time_slice: Ok(_) => { break; } - Err(e) => unsafe { - log!("work_loop error {:?}", e); - WORK_IN_PROGRESS = None - }, + Err(e) => handle_throw(root.clone(), e), }; } @@ -471,4 +487,52 @@ fn complete_unit_of_work(fiber: Rc>) { } } } -// } + +fn handle_throw(root: Rc>, thrown_value: JsValue) { + unsafe { + WORK_IN_PROGRESS_SUSPENDED_REASON = SUSPENDED_ON_DATA; + WORK_IN_PROGRESS_THROWN_VALUE = Some(thrown_value); + } +} + +fn throw_and_unwind_work_loop( + root: Rc>, + unit_of_work: Rc>, + thrown_value: JsValue, + lane: Lane, +) { + throw_exception(root.clone(), thrown_value, lane); + unwind_unit_of_work(unit_of_work); +} + +fn unwind_unit_of_work(unit_of_work: Rc>) { + let mut incomplete_work = Some(unit_of_work); + loop { + let unwrapped_work = incomplete_work.clone().unwrap(); + let next = unwind_work(unwrapped_work.clone()); + + if next.is_some() { + let next = next.unwrap(); + next.borrow_mut().flags &= get_host_effect_mask(); + return; + } + + let return_fiber = unwrapped_work.borrow()._return.clone(); + if return_fiber.is_some() { + let return_fiber = return_fiber.clone().unwrap(); + // Todo why + return_fiber.borrow_mut().deletions = vec![]; + } + + incomplete_work = return_fiber.clone(); + + if incomplete_work.is_none() { + break; + } + } + + unsafe { + WORK_IN_PROGRESS = None; + WORK_IN_PROGRESS_ROOT_EXIT_STATUS = ROOT_DID_NOT_COMPLETE; + } +} From e59dd3ce90d89960b121d54260780f7b9d6dccc0 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Thu, 22 Aug 2024 20:58:40 +0800 Subject: [PATCH 5/6] suspense phase1 --- packages/react-reconciler/src/fiber_throw.rs | 21 +++++++++++++++++--- packages/react-reconciler/src/work_loop.rs | 4 ++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/react-reconciler/src/fiber_throw.rs b/packages/react-reconciler/src/fiber_throw.rs index 7705ed0..0e3e524 100644 --- a/packages/react-reconciler/src/fiber_throw.rs +++ b/packages/react-reconciler/src/fiber_throw.rs @@ -1,13 +1,26 @@ use std::{cell::RefCell, rc::Rc}; -use shared::{derive_from_js_value, type_of}; -use wasm_bindgen::JsValue; +use shared::{derive_from_js_value, log, type_of}; +use wasm_bindgen::{prelude::Closure, JsCast, JsValue}; +use web_sys::js_sys::Function; use crate::{ fiber::FiberRootNode, fiber_flags::Flags, fiber_lanes::Lane, - suspense_context::get_suspense_handler, + suspense_context::get_suspense_handler, work_loop::ensure_root_is_scheduled, }; +fn attach_ping_listener(root: Rc>, wakeable: JsValue, lane: Lane) { + let then_value = derive_from_js_value(&wakeable, "then"); + let then = then_value.dyn_ref::().unwrap(); + let closure = Closure::wrap(Box::new(move || { + root.clone().borrow_mut().mark_root_updated(lane.clone()); + ensure_root_is_scheduled(root.clone()); + }) as Box); + let ping = closure.as_ref().unchecked_ref::().clone(); + then.call2(&JsValue::null(), &ping, &ping) + .expect("failed to call then function"); +} + pub fn throw_exception(root: Rc>, value: JsValue, lane: Lane) { if !value.is_null() && type_of(&value, "object") @@ -18,5 +31,7 @@ pub fn throw_exception(root: Rc>, value: JsValue, lane: L let suspense_boundary = suspense_boundary.unwrap(); suspense_boundary.borrow_mut().flags |= Flags::ShouldCapture; } + + attach_ping_listener(root, value, lane) } } diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 191c5bb..f8a69b4 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -97,7 +97,7 @@ pub fn mark_update_lane_from_fiber_to_root( None } -fn ensure_root_is_scheduled(root: Rc>) { +pub fn ensure_root_is_scheduled(root: Rc>) { let root_cloned = root.clone(); let update_lanes = root_cloned.borrow().get_next_lanes(); let existing_callback = root_cloned.borrow().callback_node.clone(); @@ -510,10 +510,10 @@ fn unwind_unit_of_work(unit_of_work: Rc>) { loop { let unwrapped_work = incomplete_work.clone().unwrap(); let next = unwind_work(unwrapped_work.clone()); - if next.is_some() { let next = next.unwrap(); next.borrow_mut().flags &= get_host_effect_mask(); + unsafe { WORK_IN_PROGRESS = Some(next) }; return; } From 4439356d7040781297749fb1de305047c11fc923 Mon Sep 17 00:00:00 2001 From: youxingzhi Date: Mon, 26 Aug 2024 15:10:02 +0800 Subject: [PATCH 6/6] fix bailout for Suspense --- examples/hello-world/src/suspense/index.tsx | 3 +-- packages/react-reconciler/src/fiber.rs | 6 ++++-- packages/react-reconciler/src/fiber_throw.rs | 5 +++-- packages/react-reconciler/src/work_loop.rs | 8 +++++++- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/examples/hello-world/src/suspense/index.tsx b/examples/hello-world/src/suspense/index.tsx index 93d990c..a4f5393 100644 --- a/examples/hello-world/src/suspense/index.tsx +++ b/examples/hello-world/src/suspense/index.tsx @@ -10,6 +10,5 @@ export default function App() { function Child() { debugger - throw Promise.resolve(1) - return

Child

+ throw new Promise((resolve) => setTimeout(resolve, 1000)) } diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index b1d4df7..415cb56 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -83,10 +83,12 @@ impl Debug for FiberNode { WorkTag::HostRoot => { write!( f, - "{:?}(subtreeFlags:{:?}),flags:{:?})", + "{:?}(flags:{:?},subtreeFlags:{:?}),lanes:{:?},childLanes:{:?})", WorkTag::HostRoot, + self.flags, self.subtree_flags, - self.flags + self.lanes, + self.child_lanes ) .expect("print error"); } diff --git a/packages/react-reconciler/src/fiber_throw.rs b/packages/react-reconciler/src/fiber_throw.rs index 0e3e524..b206495 100644 --- a/packages/react-reconciler/src/fiber_throw.rs +++ b/packages/react-reconciler/src/fiber_throw.rs @@ -1,6 +1,6 @@ use std::{cell::RefCell, rc::Rc}; -use shared::{derive_from_js_value, log, type_of}; +use shared::{derive_from_js_value, type_of}; use wasm_bindgen::{prelude::Closure, JsCast, JsValue}; use web_sys::js_sys::Function; @@ -17,7 +17,8 @@ fn attach_ping_listener(root: Rc>, wakeable: JsValue, lan ensure_root_is_scheduled(root.clone()); }) as Box); let ping = closure.as_ref().unchecked_ref::().clone(); - then.call2(&JsValue::null(), &ping, &ping) + closure.forget(); + then.call2(&wakeable, &ping, &ping) .expect("failed to call then function"); } diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index f8a69b4..3d710b4 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -180,6 +180,12 @@ fn render_root(root: Rc>, lane: Lane, should_time_slice: WORK_IN_PROGRESS_SUSPENDED_REASON = NOT_SUSPENDED; WORK_IN_PROGRESS_THROWN_VALUE = None; + // TODO + mark_update_lane_from_fiber_to_root( + WORK_IN_PROGRESS.clone().unwrap(), + lane.clone(), + ); + throw_and_unwind_work_loop( root.clone(), WORK_IN_PROGRESS.clone().unwrap(), @@ -501,7 +507,7 @@ fn throw_and_unwind_work_loop( thrown_value: JsValue, lane: Lane, ) { - throw_exception(root.clone(), thrown_value, lane); + throw_exception(root.clone(), thrown_value, lane.clone()); unwind_unit_of_work(unit_of_work); }