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 './useCallback'
export {default} from './perf'
32 changes: 32 additions & 0 deletions examples/hello-world/src/perf/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {useState} from 'react'

function Child({num}) {
console.log('Child Render')
return <div>Child {num}</div>
}

function Parent() {
const [num, setNum] = useState(1)
console.log('Parent render')
return (
<div onClick={() => setNum(2)}>
<Child num={num} />
</div>
// <div onClick={() => setNum(2)}>
// Parent {num}
// <Child num={num} />
// </div>
)
}

export default function App() {
console.log('App render')
return (
<div>
App
<Parent />
</div>
)
}

//https://juejin.cn/post/7073692220313829407?searchId=20240719185830A176472F8B81316DB83C
104 changes: 98 additions & 6 deletions packages/react-reconciler/src/begin_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,103 @@ use std::rc::Rc;

use wasm_bindgen::JsValue;

use shared::derive_from_js_value;
use shared::{derive_from_js_value, is_dev, log};
use web_sys::js_sys::Object;

use crate::child_fiber::{mount_child_fibers, reconcile_child_fibers};
use crate::child_fiber::{clone_child_fiblers, mount_child_fibers, reconcile_child_fibers};
use crate::fiber::{FiberNode, MemoizedState};
use crate::fiber_flags::Flags;
use crate::fiber_hooks::render_with_hooks;
use crate::fiber_lanes::Lane;
use crate::fiber_hooks::{bailout_hook, render_with_hooks};
use crate::fiber_lanes::{include_some_lanes, Lane};
use crate::update_queue::{process_update_queue, ReturnOfProcessUpdateQueue};
use crate::work_tags::WorkTag;

static mut DID_RECEIVE_UPDATE: bool = false;

pub fn mark_wip_received_update() {
unsafe { DID_RECEIVE_UPDATE = true };
}

fn bailout_on_already_finished_work(
wip: Rc<RefCell<FiberNode>>,
render_lane: Lane,
) -> Option<Rc<RefCell<FiberNode>>> {
// log!(
// "tag:{:?} child_lanes:{:?} render_lanes:{:?} result:{:?}",
// wip.borrow().tag,
// wip.borrow().child_lanes.clone(),
// render_lane,
// wip.borrow().child_lanes.clone() & render_lane.clone()
// );
if !include_some_lanes(wip.borrow().child_lanes.clone(), render_lane) {
if is_dev() {
log!("bailout the whole subtree {:?}", wip);
}
return None;
}
if is_dev() {
log!("bailout current fiber {:?}", wip);
}
clone_child_fiblers(wip.clone());
wip.borrow().child.clone()
}

fn check_scheduled_update_or_context(current: Rc<RefCell<FiberNode>>, render_lane: Lane) -> bool {
let update_lanes = current.borrow().lanes.clone();
if include_some_lanes(update_lanes, render_lane) {
return true;
}
false
}

pub fn begin_work(
work_in_progress: Rc<RefCell<FiberNode>>,
render_lane: Lane,
) -> Result<Option<Rc<RefCell<FiberNode>>>, JsValue> {
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
// );
// // }
return Ok(bailout_on_already_finished_work(
work_in_progress,
render_lane,
));
}
}
}

work_in_progress.borrow_mut().lanes = Lane::NoLane;
// if current.is_some() {
// let current = current.clone().unwrap();
// current.borrow_mut().lanes = Lane::NoLane;
// }

let tag = work_in_progress.clone().borrow().tag.clone();
return match tag {
WorkTag::FunctionComponent => {
Expand All @@ -33,7 +115,17 @@ fn update_function_component(
work_in_progress: Rc<RefCell<FiberNode>>,
render_lane: Lane,
) -> Result<Option<Rc<RefCell<FiberNode>>>, JsValue> {
let next_children = render_with_hooks(work_in_progress.clone(), render_lane)?;
let next_children = render_with_hooks(work_in_progress.clone(), render_lane.clone())?;

let current = work_in_progress.borrow().alternate.clone();
if current.is_some() && unsafe { !DID_RECEIVE_UPDATE } {
bailout_hook(work_in_progress.clone(), render_lane.clone());
return Ok(bailout_on_already_finished_work(
work_in_progress,
render_lane,
));
}

reconcile_children(work_in_progress.clone(), Some(next_children));
Ok(work_in_progress.clone().borrow().child.clone())
}
Expand Down Expand Up @@ -61,7 +153,7 @@ fn update_host_root(

{
let ReturnOfProcessUpdateQueue { memoized_state, .. } =
process_update_queue(base_state, pending, render_lane);
process_update_queue(base_state, pending, render_lane, None);
work_in_progress.clone().borrow_mut().memoized_state = memoized_state;
}

Expand Down
45 changes: 40 additions & 5 deletions packages/react-reconciler/src/child_fiber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ fn reconcile_single_text_node(
Rc::new(RefCell::new(created))
}

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

impl PartialEq for Key {
Expand Down Expand Up @@ -212,7 +213,7 @@ fn update_from_map(
should_track_effects: bool,
) -> Option<Rc<RefCell<FiberNode>>> {
let key_to_use;
if type_of(element, "string") || type_of(element, "null") {
if type_of(element, "string") || type_of(element, "null") || type_of(element, "number") {
key_to_use = JsValue::from(index);
} else {
let key = derive_from_js_value(element, "key");
Expand All @@ -224,6 +225,7 @@ fn update_from_map(
let before = existing_children.get(&Key(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()));
Expand Down Expand Up @@ -268,7 +270,6 @@ fn update_from_map(
element,
))));
}
// panic!("update_from_map unsupported");
None
}

Expand Down Expand Up @@ -296,18 +297,19 @@ fn reconcile_children_array(
existing_children.insert(Key(key_to_use), current_rc.clone());
current = current_rc.borrow().sibling.clone();
}

// log!("existing_children {:?}", existing_children.keys());
let length = new_child.length();
for i in 0..length {
let after = new_child.get(i);
// log!("after {:?}", after);
let new_fiber = update_from_map(
return_fiber.clone(),
&mut existing_children,
i,
&after,
should_track_effects,
);

// log!("new_fiber {:?}", new_fiber);
if new_fiber.is_none() {
continue;
}
Expand Down Expand Up @@ -348,7 +350,11 @@ fn reconcile_children_array(
for (_, fiber) in existing_children {
delete_child(return_fiber.clone(), fiber, should_track_effects);
}

log!(
"first_new_fiber {:?} {:?}",
first_new_fiber,
first_new_fiber.clone().unwrap().borrow().sibling
);
first_new_fiber
}

Expand Down Expand Up @@ -398,6 +404,35 @@ fn _reconcile_child_fibers(
delete_remaining_children(return_fiber, current_first_child, should_track_effects)
}

pub fn clone_child_fiblers(wip: Rc<RefCell<FiberNode>>) {
if wip.borrow().child.is_none() {
return;
}

let mut current_child = { wip.borrow().child.clone().unwrap() };
let pending_props = { current_child.borrow().pending_props.clone() };
let mut new_child = FiberNode::create_work_in_progress(current_child.clone(), pending_props);
wip.borrow_mut().child = Some(new_child.clone());
new_child.borrow_mut()._return = Some(wip.clone());

while current_child.borrow().sibling.is_some() {
let sibling = { current_child.borrow().sibling.clone().unwrap() };
let pending_props = { sibling.borrow().pending_props.clone() };
let new_slibing = FiberNode::create_work_in_progress(sibling.clone(), pending_props);
new_slibing.borrow_mut()._return = Some(wip.clone());
// log!(
// "new {:?} {:?} {:?}",
// new_slibing,
// new_slibing.borrow()._return,
// sibling.borrow()._return
// );
new_child.borrow_mut().sibling = Some(new_slibing.clone());

current_child = sibling;
new_child = new_slibing;
}
}

pub fn reconcile_child_fibers(
return_fiber: Rc<RefCell<FiberNode>>,
current_first_child: Option<Rc<RefCell<FiberNode>>>,
Expand Down
6 changes: 6 additions & 0 deletions packages/react-reconciler/src/commit_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ fn commit_mutation_effects_on_fiber(
finished_work.borrow_mut().flags -= Flags::ChildDeletion;
}

// log!(
// "finished_work {:?} {:?}",
// finished_work,
// finished_work.borrow().alternate
// );
if flags.contains(Flags::Update) {
commit_update(finished_work.clone());
finished_work.borrow_mut().flags -= Flags::Update;
Expand Down Expand Up @@ -285,6 +290,7 @@ fn commit_update(finished_work: Rc<RefCell<FiberNode>>) {
WorkTag::HostText => {
let new_content = derive_from_js_value(&cloned.borrow().pending_props, "content");
let state_node = FiberNode::derive_state_node(finished_work.clone());
log!("commit_update {:?} {:?}", state_node, new_content);
if let Some(state_node) = state_node.clone() {
unsafe {
HOST_CONFIG
Expand Down
31 changes: 28 additions & 3 deletions packages/react-reconciler/src/complete_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use shared::{derive_from_js_value, log};

use crate::fiber::{FiberNode, StateNode};
use crate::fiber_flags::Flags;
use crate::fiber_lanes::{merge_lanes, Lane};
use crate::work_tags::WorkTag;
use crate::HostConfig;

Expand Down Expand Up @@ -97,14 +98,24 @@ impl CompleteWork {

fn bubble_properties(&self, complete_work: Rc<RefCell<FiberNode>>) {
let mut subtree_flags = Flags::NoFlags;
let mut new_child_lanes = Lane::NoLane;
{
let mut child = complete_work.clone().borrow().child.clone();

while child.is_some() {
let child_rc = child.clone().unwrap().clone();
{
let child_borrowed = child_rc.borrow();
subtree_flags |= child_borrowed.subtree_flags.clone();
subtree_flags |= child_borrowed.flags.clone();

new_child_lanes = merge_lanes(
new_child_lanes,
merge_lanes(
child_borrowed.lanes.clone(),
child_borrowed.child_lanes.clone(),
),
)
}
{
child_rc.borrow_mut()._return = Some(complete_work.clone());
Expand All @@ -114,6 +125,7 @@ impl CompleteWork {
}

complete_work.clone().borrow_mut().subtree_flags |= subtree_flags.clone();
complete_work.clone().borrow_mut().child_lanes |= new_child_lanes.clone();
}

fn mark_update(fiber: Rc<RefCell<FiberNode>>) {
Expand All @@ -131,15 +143,18 @@ impl CompleteWork {
match tag {
WorkTag::FunctionComponent => {
self.bubble_properties(work_in_progress.clone());
// log!("bubble_properties function {:?}", work_in_progress.clone());
None
}
WorkTag::HostRoot => {
self.bubble_properties(work_in_progress.clone());
// log!("bubble_properties HostRoot {:?}", work_in_progress.clone());
None
}
WorkTag::HostComponent => {
if current.is_some() && work_in_progress_cloned.borrow().state_node.is_some() {
log!("update properties");
// todo compare
// CompleteWork::mark_update(work_in_progress.clone());
let current = current.unwrap();
if !Object::is(
&current.borrow()._ref,
Expand Down Expand Up @@ -167,6 +182,10 @@ impl CompleteWork {
}

self.bubble_properties(work_in_progress.clone());
// log!(
// "bubble_properties HostComponent {:?}",
// work_in_progress.clone()
// );
None
}
WorkTag::HostText => {
Expand All @@ -175,8 +194,14 @@ impl CompleteWork {
&current.clone().unwrap().clone().borrow().memoized_props,
"content",
);
let new_test = derive_from_js_value(&new_props, "content");
if !Object::is(&old_text, &new_test) {
let new_text = derive_from_js_value(&new_props, "content");
// log!(
// "complete host {:?} {:?} {:?}",
// work_in_progress_cloned,
// old_text,
// new_text
// );
if !Object::is(&old_text, &new_text) {
CompleteWork::mark_update(work_in_progress.clone());
}
} else {
Expand Down
Loading