Skip to content

Blog 25 - Suspense(2) - use hook #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: feat-suspense-1
Choose a base branch
from
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
24 changes: 21 additions & 3 deletions examples/hello-world/src/suspense/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
import {Suspense} from 'react'
import {Suspense, use} from 'react'

const delay = (t) =>
new Promise((r) => {
setTimeout(r, t)
})

const cachePool: any[] = []

function fetchData(id, timeout) {
const cache = cachePool[id]
if (cache) {
return cache
}
return (cachePool[id] = delay(timeout).then(() => {
return {data: Math.random().toFixed(2) * 100}
}))
}

export default function App() {
return (
Expand All @@ -9,6 +26,7 @@ export default function App() {
}

function Child() {
debugger
throw new Promise((resolve) => setTimeout(resolve, 1000))
const {data} = use(fetchData(1, 1000))

return <span>{data}</span>
}
112 changes: 53 additions & 59 deletions packages/react-reconciler/src/begin_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,56 +51,48 @@ pub fn begin_work(
work_in_progress: Rc<RefCell<FiberNode>>,
render_lane: Lane,
) -> Result<Option<Rc<RefCell<FiberNode>>>, JsValue> {
log!("begin_work {:?}", work_in_progress);
log!("begin_work {:?}", work_in_progress.clone());
unsafe {
DID_RECEIVE_UPDATE = false;
};
let current = { work_in_progress.borrow().alternate.clone() };

if current.is_some() {
let current = current.clone().unwrap();
let old_props = current.borrow().memoized_props.clone();
let old_type = current.borrow()._type.clone();
let new_props = work_in_progress.borrow().pending_props.clone();
let new_type = work_in_progress.borrow()._type.clone();
if !Object::is(&old_props, &new_props) || !Object::is(&old_type, &new_type) {
unsafe { DID_RECEIVE_UPDATE = true }
} else {
let has_scheduled_update_or_context =
check_scheduled_update_or_context(current.clone(), render_lane.clone());
// The current fiber lane is not included in render_lane
// TODO context
if !has_scheduled_update_or_context {
unsafe { DID_RECEIVE_UPDATE = false }
// // if current.is_some() {
// let c = current.clone();
// log!(
// "current tag:{:?} lanes:{:?} child_lanes:{:?} render_lane:{:?}",
// c.borrow().tag,
// c.borrow().lanes,
// c.borrow().child_lanes,
// render_lane
// );
// // }
match work_in_progress.borrow().tag {
WorkTag::ContextProvider => {
let new_value = derive_from_js_value(
&work_in_progress.borrow().memoized_props,
"value",
);
let context =
derive_from_js_value(&work_in_progress.borrow()._type, "_context");
push_provider(&context, new_value);
}
_ => {}
}
return Ok(bailout_on_already_finished_work(
work_in_progress,
render_lane,
));
}
}
}
// TODO work with suspense
// let current = { work_in_progress.borrow().alternate.clone() };

// if current.is_some() {
// let current = current.clone().unwrap();
// let old_props = current.borrow().memoized_props.clone();
// let old_type = current.borrow()._type.clone();
// let new_props = work_in_progress.borrow().pending_props.clone();
// let new_type = work_in_progress.borrow()._type.clone();
// if !Object::is(&old_props, &new_props) || !Object::is(&old_type, &new_type) {
// unsafe { DID_RECEIVE_UPDATE = true }
// } else {
// let has_scheduled_update_or_context =
// check_scheduled_update_or_context(current.clone(), render_lane.clone());
// // The current fiber lane is not included in render_lane
// // TODO context
// if !has_scheduled_update_or_context {
// unsafe { DID_RECEIVE_UPDATE = false }
// match work_in_progress.borrow().tag {
// WorkTag::ContextProvider => {
// let new_value = derive_from_js_value(
// &work_in_progress.borrow().memoized_props,
// "value",
// );
// let context =
// derive_from_js_value(&work_in_progress.borrow()._type, "_context");
// push_provider(&context, new_value);
// }
// _ => {}
// }
// return Ok(bailout_on_already_finished_work(
// work_in_progress,
// render_lane,
// ));
// }
// }
// }

work_in_progress.borrow_mut().lanes = Lane::NoLane;
// if current.is_some() {
Expand All @@ -124,7 +116,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 => Ok(update_offscreen_component(work_in_progress)),
WorkTag::OffscreenComponent => Ok(update_offscreen_component(work_in_progress.clone())),
};
}

Expand Down Expand Up @@ -161,8 +153,9 @@ fn update_suspense_fallback_children(
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 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());
Expand Down Expand Up @@ -219,8 +212,9 @@ fn update_suspense_primary_children(
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 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());
Expand All @@ -237,13 +231,13 @@ fn update_suspense_primary_children(

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;
let mut deletions = { work_in_progress.borrow().deletions.clone() };
if deletions.is_empty() {
work_in_progress.borrow_mut().deletions = vec![current_fallback_child_fragment];
work_in_progress.borrow_mut().flags != Flags::ChildDeletion;
work_in_progress.borrow_mut().flags |= Flags::ChildDeletion;
} else {
let deletions = &mut work_in_progress.borrow_mut().deletions;
deletions.push(current_primary_child_fragment)
deletions.push(current_fallback_child_fragment)
}
}

Expand All @@ -257,8 +251,8 @@ fn update_suspense_component(
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;
let flags = { work_in_progress.borrow().flags.clone() };
let did_suspend = (flags & Flags::DidCapture) != Flags::NoFlags;

if did_suspend {
show_fallback = true;
Expand Down Expand Up @@ -301,14 +295,14 @@ fn update_suspense_component(
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_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();
let next_children = { work_in_progress.borrow().pending_props.clone() };
reconcile_children(work_in_progress.clone(), Some(next_children));
work_in_progress.borrow().child.clone()
}
Expand Down Expand Up @@ -392,7 +386,7 @@ fn update_function_component(
let next_children =
render_with_hooks(work_in_progress.clone(), Component, render_lane.clone())?;

let current = work_in_progress.borrow().alternate.clone();
let current = { work_in_progress.borrow().alternate.clone() };
log!("{:?} {:?}", work_in_progress.clone(), unsafe {
DID_RECEIVE_UPDATE
});
Expand Down
51 changes: 15 additions & 36 deletions packages/react-reconciler/src/child_fiber.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::rc::Rc;

use wasm_bindgen::{JsCast, JsValue};
Expand All @@ -12,6 +11,7 @@ use crate::fiber::FiberNode;
use crate::fiber_flags::Flags;
use crate::work_tags::WorkTag;
use crate::work_tags::WorkTag::HostText;
use crate::JsValueKey;

fn use_fiber(fiber: Rc<RefCell<FiberNode>>, pending_props: JsValue) -> Rc<RefCell<FiberNode>> {
let clone = FiberNode::create_work_in_progress(fiber, pending_props);
Expand Down Expand Up @@ -133,15 +133,15 @@ fn reconcile_single_element(
}
}

let mut fiber ;
let mut fiber;
if derive_from_js_value(&element, "type") == REACT_FRAGMENT_TYPE {
let props = derive_from_js_value(&element, "props");
let children = derive_from_js_value(&props, "children");
fiber = FiberNode::create_fiber_from_fragment(children, key);
} else {
fiber = FiberNode::create_fiber_from_element(element);
}

fiber._return = Some(return_fiber.clone());
Rc::new(RefCell::new(fiber))
}
Expand Down Expand Up @@ -190,35 +190,12 @@ fn reconcile_single_text_node(
Rc::new(RefCell::new(created))
}

#[derive(Clone, Debug)]
struct Key(JsValue);

impl PartialEq for Key {
fn eq(&self, other: &Self) -> bool {
Object::is(&self.0, &other.0)
}
}

impl Eq for Key {}

impl Hash for Key {
fn hash<H: Hasher>(&self, state: &mut H) {
if self.0.is_string() {
self.0.as_string().unwrap().hash(state)
} else if let Some(n) = self.0.as_f64() {
n.to_bits().hash(state)
} else if self.0.is_null() {
"null".hash(state)
}
}
}

fn update_fragment(
return_fiber: Rc<RefCell<FiberNode>>,
current: Option<Rc<RefCell<FiberNode>>>,
elements: JsValue,
key: Key,
existing_children: &mut HashMap<Key, Rc<RefCell<FiberNode>>>,
key: JsValueKey,
existing_children: &mut HashMap<JsValueKey, Rc<RefCell<FiberNode>>>,
) -> Rc<RefCell<FiberNode>> {
let fiber;
if current.is_none() || current.clone().unwrap().borrow().tag != WorkTag::Fragment {
Expand All @@ -235,7 +212,7 @@ fn update_fragment(

fn update_from_map(
return_fiber: Rc<RefCell<FiberNode>>,
existing_children: &mut HashMap<Key, Rc<RefCell<FiberNode>>>,
existing_children: &mut HashMap<JsValueKey, Rc<RefCell<FiberNode>>>,
index: u32,
element: &JsValue,
should_track_effects: bool,
Expand All @@ -255,13 +232,15 @@ fn update_from_map(
false => key.clone(),
}
}
let before = existing_children.get(&Key(key_to_use.clone())).clone();
let before = existing_children
.get(&JsValueKey(key_to_use.clone()))
.clone();
if type_of(element, "null") || type_of(element, "string") || type_of(element, "number") {
let props = create_props_with_content(element.clone());
// log!("update_from_map {:?}", props);
if before.is_some() {
let before = (*before.clone().unwrap()).clone();
existing_children.remove(&Key(key_to_use.clone()));
existing_children.remove(&JsValueKey(key_to_use.clone()));
if before.borrow().tag == HostText {
return Some(use_fiber(before.clone(), props.clone()));
} else {
Expand All @@ -287,7 +266,7 @@ fn update_from_map(
return_fiber,
before,
(*element).clone(),
Key(key_to_use.clone()),
JsValueKey(key_to_use.clone()),
existing_children,
));
} else if type_of(element, "object") && !element.is_null() {
Expand All @@ -301,14 +280,14 @@ fn update_from_map(
return_fiber,
before,
(*element).clone(),
Key(key_to_use.clone()),
JsValueKey(key_to_use.clone()),
existing_children,
));
}

if before.is_some() {
let before = (*before.clone().unwrap()).clone();
existing_children.remove(&Key(key_to_use.clone()));
existing_children.remove(&JsValueKey(key_to_use.clone()));
if Object::is(
&before.borrow()._type,
&derive_from_js_value(&(*element).clone(), "type"),
Expand Down Expand Up @@ -346,15 +325,15 @@ fn reconcile_children_array(
// 创建的第一个fiber
let mut first_new_fiber: Option<Rc<RefCell<FiberNode>>> = None;

let mut existing_children: HashMap<Key, Rc<RefCell<FiberNode>>> = HashMap::new();
let mut existing_children: HashMap<JsValueKey, Rc<RefCell<FiberNode>>> = HashMap::new();
let mut current = current_first_child;
while current.is_some() {
let current_rc = current.unwrap();
let key_to_use = match current_rc.clone().borrow().key.is_null() {
true => JsValue::from(current_rc.borrow().index),
false => current_rc.borrow().key.clone(),
};
existing_children.insert(Key(key_to_use), current_rc.clone());
existing_children.insert(JsValueKey(key_to_use), current_rc.clone());
current = current_rc.borrow().sibling.clone();
}
// log!("existing_children {:?}", existing_children.keys());
Expand Down
Loading