Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/hello-world/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export {default} from './fragment'
export {default} from './suspense'
14 changes: 14 additions & 0 deletions examples/hello-world/src/suspense/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {Suspense} from 'react'

export default function App() {
return (
<Suspense fallback={<div>loading</div>}>
<Child />
</Suspense>
)
}

function Child() {
debugger
throw new Promise((resolve) => setTimeout(resolve, 1000))
}
184 changes: 183 additions & 1 deletion packages/react-reconciler/src/begin_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ 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};
use crate::fiber_context::{prepare_to_read_context, propagate_context_change, push_provider};
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;

Expand Down Expand Up @@ -122,9 +123,190 @@ 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 => Ok(update_offscreen_component(work_in_progress)),
};
}

fn mount_suspense_fallback_children(
work_in_progress: Rc<RefCell<FiberNode>>,
primary_children: JsValue,
fallback_children: JsValue,
) -> Rc<RefCell<FiberNode>> {
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_fallback_children(
work_in_progress: Rc<RefCell<FiberNode>>,
primary_children: JsValue,
fallback_children: JsValue,
) -> Rc<RefCell<FiberNode>> {
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<RefCell<FiberNode>>,
primary_children: JsValue,
) -> Rc<RefCell<FiberNode>> {
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<RefCell<FiberNode>>,
primary_children: JsValue,
) -> Rc<RefCell<FiberNode>> {
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<RefCell<FiberNode>>,
) -> Option<Rc<RefCell<FiberNode>>> {
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());
log!("show_fallback {:?}", show_fallback);
if current.is_none() {
if show_fallback {
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(),
));
}
}
}

fn update_offscreen_component(
work_in_progress: Rc<RefCell<FiberNode>>,
) -> Option<Rc<RefCell<FiberNode>>> {
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<RefCell<FiberNode>>) -> Option<Rc<RefCell<FiberNode>>> {
let next_children = work_in_progress.borrow().pending_props.clone();
reconcile_children(work_in_progress.clone(), Some(next_children));
Expand Down
25 changes: 25 additions & 0 deletions packages/react-reconciler/src/complete_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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(
// &current_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
Expand Down
29 changes: 25 additions & 4 deletions packages/react-reconciler/src/fiber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -81,9 +83,12 @@ impl Debug for FiberNode {
WorkTag::HostRoot => {
write!(
f,
"{:?}(subtreeFlags:{:?})",
"{:?}(flags:{:?},subtreeFlags:{:?}),lanes:{:?},childLanes:{:?})",
WorkTag::HostRoot,
self.subtree_flags
self.flags,
self.subtree_flags,
self.lanes,
self.child_lanes
)
.expect("print error");
}
Expand Down Expand Up @@ -142,6 +147,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())
}
Expand All @@ -153,7 +167,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");
Expand Down Expand Up @@ -276,6 +293,8 @@ pub struct FiberRootNode {
pub finished_work: Option<Rc<RefCell<FiberNode>>>,
pub pending_lanes: Lane,
pub finished_lanes: Lane,
pub suspended_lanes: Lane,
pub pinged_lanes: Lane,
pub callback_node: Option<Task>,
pub callback_priority: Lane,
pub pending_passive_effects: Rc<RefCell<PendingPassiveEffects>>,
Expand All @@ -295,6 +314,8 @@ impl FiberRootNode {
})),
callback_node: None,
callback_priority: Lane::NoLane,
pinged_lanes: Lane::NoLane,
suspended_lanes: Lane::NoLane,
}
}

Expand Down
30 changes: 19 additions & 11 deletions packages/react-reconciler/src/fiber_flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +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 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
}
}

Expand All @@ -29,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
}
Loading