diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx index ac455c1..08e9d72 100644 --- a/examples/hello-world/src/App.tsx +++ b/examples/hello-world/src/App.tsx @@ -1 +1 @@ -export {default} from './memo' +export {default} from './fragment' diff --git a/examples/hello-world/src/fragment/index.tsx b/examples/hello-world/src/fragment/index.tsx new file mode 100644 index 0000000..258c857 --- /dev/null +++ b/examples/hello-world/src/fragment/index.tsx @@ -0,0 +1,19 @@ +import {useState} from 'react' + +export default function App() { + const [num, setNum] = useState(100) + const arr = + num % 2 === 0 + ? [
  • 1
  • ,
  • 2
  • ,
  • 3
  • ] + : [
  • 3
  • ,
  • 2
  • ,
  • 1
  • ] + return ( + + ) +} diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs index fac3c81..bea6426 100644 --- a/packages/react-reconciler/src/begin_work.rs +++ b/packages/react-reconciler/src/begin_work.rs @@ -121,9 +121,16 @@ pub fn begin_work( render_lane.clone(), )), WorkTag::MemoComponent => update_memo_component(work_in_progress.clone(), render_lane), + WorkTag::Fragment => Ok(update_fragment(work_in_progress.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)); + work_in_progress.borrow().child.clone() +} + fn update_memo_component( work_in_progress: Rc>, render_lane: Lane, diff --git a/packages/react-reconciler/src/child_fiber.rs b/packages/react-reconciler/src/child_fiber.rs index 4d938ee..c90cfb9 100644 --- a/packages/react-reconciler/src/child_fiber.rs +++ b/packages/react-reconciler/src/child_fiber.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use wasm_bindgen::{JsCast, JsValue}; use web_sys::js_sys::{Array, Object, Reflect}; -use shared::{derive_from_js_value, log, type_of, REACT_ELEMENT_TYPE}; +use shared::{derive_from_js_value, log, type_of, REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE}; use crate::fiber::FiberNode; use crate::fiber_flags::Flags; @@ -133,7 +133,15 @@ fn reconcile_single_element( } } - let mut fiber = FiberNode::create_fiber_from_element(element); + 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)) } @@ -205,6 +213,26 @@ impl Hash for Key { } } +fn update_fragment( + return_fiber: Rc>, + current: Option>>, + elements: JsValue, + key: Key, + existing_children: &mut HashMap>>, +) -> Rc> { + let fiber; + if current.is_none() || current.clone().unwrap().borrow().tag != WorkTag::Fragment { + fiber = Rc::new(RefCell::new(FiberNode::create_fiber_from_fragment( + elements, key.0, + ))); + } else { + existing_children.remove(&key); + fiber = use_fiber(current.clone().unwrap(), elements); + } + fiber.borrow_mut()._return = Some(return_fiber); + fiber +} + fn update_from_map( return_fiber: Rc>, existing_children: &mut HashMap>>, @@ -250,31 +278,58 @@ fn update_from_map( JsValue::null(), )))) }; + } else if element.is_array() { + let before = match before { + Some(before) => Some((*before).clone()), + None => None, + }; + return Some(update_fragment( + return_fiber, + before, + (*element).clone(), + Key(key_to_use.clone()), + existing_children, + )); } else if type_of(element, "object") && !element.is_null() { - if derive_from_js_value(&(*element).clone(), "$$typeof") != REACT_ELEMENT_TYPE { - panic!("Undefined $$typeof"); - } - - if before.is_some() { - let before = (*before.clone().unwrap()).clone(); - existing_children.remove(&Key(key_to_use.clone())); - if Object::is( - &before.borrow()._type, - &derive_from_js_value(&(*element).clone(), "type"), - ) { - return Some(use_fiber( - before.clone(), - derive_from_js_value(element, "props"), + if derive_from_js_value(&(*element).clone(), "$$typeof") == REACT_ELEMENT_TYPE { + if derive_from_js_value(&(*element).clone(), "type") == REACT_FRAGMENT_TYPE { + let before = match before { + Some(before) => Some((*before).clone()), + None => None, + }; + return Some(update_fragment( + return_fiber, + before, + (*element).clone(), + Key(key_to_use.clone()), + existing_children, )); - } else { - delete_child(return_fiber, before, should_track_effects); } - } - return Some(Rc::new(RefCell::new(FiberNode::create_fiber_from_element( - element, - )))); + if before.is_some() { + let before = (*before.clone().unwrap()).clone(); + existing_children.remove(&Key(key_to_use.clone())); + if Object::is( + &before.borrow()._type, + &derive_from_js_value(&(*element).clone(), "type"), + ) { + return Some(use_fiber( + before.clone(), + derive_from_js_value(element, "props"), + )); + } + + // else { + // delete_child(return_fiber, before, should_track_effects); + // } + } + + return Some(Rc::new(RefCell::new(FiberNode::create_fiber_from_element( + element, + )))); + } } + None } diff --git a/packages/react-reconciler/src/commit_work.rs b/packages/react-reconciler/src/commit_work.rs index ea5f388..f0c437a 100644 --- a/packages/react-reconciler/src/commit_work.rs +++ b/packages/react-reconciler/src/commit_work.rs @@ -494,8 +494,8 @@ fn get_host_parent(fiber: Rc>) -> Option>) -> Option> { let mut node = Some(fiber); 'find_sibling: loop { - let node_rc = node.clone().unwrap(); - while node_rc.borrow().sibling.is_none() { + while node.clone().unwrap().borrow().sibling.is_none() { + let node_rc = node.clone().unwrap(); let parent = node_rc.borrow()._return.clone(); let tag = parent.clone().unwrap().borrow().tag.clone(); if parent.is_none() || tag == HostComponent || tag == HostRoot { @@ -515,9 +515,10 @@ fn get_host_sibling(fiber: Rc>) -> Option> { ._return = _return; node = node_rc.borrow().sibling.clone(); - let node_rc = node.clone().unwrap(); - let tag = node_rc.borrow().tag.clone(); - while tag != HostText && tag != HostComponent { + while node.clone().unwrap().borrow().tag != HostText + && node.clone().unwrap().borrow().tag != HostComponent + { + let node_rc = node.clone().unwrap(); if node_rc.borrow().flags.contains(Flags::Placement) { continue 'find_sibling; } diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs index dcffed3..a566141 100644 --- a/packages/react-reconciler/src/fiber.rs +++ b/packages/react-reconciler/src/fiber.rs @@ -78,17 +78,6 @@ pub struct FiberNode { impl Debug for FiberNode { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { Ok(match self.tag { - WorkTag::FunctionComponent => { - write!( - f, - "{:?}(flags:{:?}, subtreeFlags:{:?}, lanes:{:?})", - self._type.as_ref(), - self.flags, - self.subtree_flags, - self.lanes - ) - .expect("print error"); - } WorkTag::HostRoot => { write!( f, @@ -98,18 +87,10 @@ impl Debug for FiberNode { ) .expect("print error"); } - WorkTag::HostComponent => { - write!( - f, - "{:?}(key:{:?}, flags:{:?}, subtreeFlags:{:?})", - self._type, self.key, self.flags, self.subtree_flags - ) - .expect("print error"); - } WorkTag::HostText => { write!( f, - "{:?}(state_node:{:?}, flags:{:?})", + "{:?}(state_node:{:?},flags:{:?})", self.tag, Reflect::get(self.pending_props.as_ref(), &JsValue::from_str("content")) .unwrap(), @@ -117,25 +98,17 @@ impl Debug for FiberNode { ) .expect("print error"); } - WorkTag::ContextProvider => { - write!( - f, - "{:?}(flags:{:?}, subtreeFlags:{:?}, lanes:{:?})", - self._type.as_ref(), - self.flags, - self.subtree_flags, - self.lanes - ) - .expect("print error"); - } - WorkTag::MemoComponent => { + _ => { write!( f, - "{:?}(flags:{:?}, subtreeFlags:{:?}, lanes:{:?})", + "{:?}(tag:{:?},key:{:?}flags:{:?},subtreeFlags:{:?},lanes:{:?},childLanes:{:?})", self._type.as_ref(), + self.tag, + self.key, self.flags, self.subtree_flags, - self.lanes + self.lanes, + self.child_lanes ) .expect("print error"); } @@ -169,6 +142,10 @@ impl FiberNode { } } + pub fn create_fiber_from_fragment(elements: JsValue, key: JsValue) -> FiberNode { + FiberNode::new(WorkTag::Fragment, elements, key, JsValue::null()) + } + pub fn create_fiber_from_element(ele: &JsValue) -> Self { let _type = derive_from_js_value(ele, "type"); let key = derive_from_js_value(ele, "key"); @@ -396,7 +373,7 @@ impl Debug for FiberRootNode { } if current_ref._return.is_some() { - write!(f, ",").expect("print error"); + write!(f, " | ").expect("print error"); } else { 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 67b8a92..0557bcb 100644 --- a/packages/react-reconciler/src/fiber_hooks.rs +++ b/packages/react-reconciler/src/fiber_hooks.rs @@ -407,7 +407,7 @@ fn update_state(_: &JsValue) -> Result, JsValue> { base_state: new_base_state, base_queue: new_base_queue, } = process_update_queue( - base_state, + base_state.clone(), base_queue, unsafe { RENDER_LANE.clone() }, Some(|update: Rc>| { @@ -474,19 +474,10 @@ fn dispatch_set_state( let lane = request_update_lane(); let mut update = create_update(action.clone(), lane.clone()); let current = { fiber.borrow().alternate.clone() }; - log!( - "dispatch_set_state {:?} {:?}", - fiber.borrow().lanes.clone(), - if current.is_none() { - Lane::NoLane - } else { - current.clone().unwrap().borrow().lanes.clone() - } - ); + log!("dispatch_set_state action:{:?}", action); if fiber.borrow().lanes == Lane::NoLane && (current.is_none() || current.unwrap().borrow().lanes == Lane::NoLane) { - log!("sdadgasd"); let current_state = update_queue.borrow().last_rendered_state.clone(); if current_state.is_none() { panic!("current state is none") diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs index 0f710e9..87ce5c9 100644 --- a/packages/react-reconciler/src/work_loop.rs +++ b/packages/react-reconciler/src/work_loop.rs @@ -72,12 +72,12 @@ pub fn mark_update_lane_from_fiber_to_root( let p = parent.clone().unwrap(); let child_lanes = { p.borrow().child_lanes.clone() }; p.borrow_mut().child_lanes = merge_lanes(child_lanes, lane.clone()); - let alternate = p.borrow().alternate.clone(); if alternate.is_some() { let alternate = alternate.unwrap(); let child_lanes = { alternate.borrow().child_lanes.clone() }; alternate.borrow_mut().child_lanes = merge_lanes(child_lanes, lane.clone()); + log!("mark_update_lane_from_fiber_to_root alternate {:?}", p); } node = parent.clone().unwrap(); @@ -198,8 +198,8 @@ fn render_root(root: Rc>, lanes: Lane, should_time_slice: }; } - // log!("render over {:?}", *root.clone().borrow()); - log!("render over {:?}", unsafe { WORK_IN_PROGRESS.clone() }); + log!("render over {:?}", *root.clone().borrow()); + // log!("render over {:?}", unsafe { WORK_IN_PROGRESS.clone() }); // log!("render over"); unsafe { diff --git a/packages/react-reconciler/src/work_tags.rs b/packages/react-reconciler/src/work_tags.rs index cfecddf..5bc9754 100644 --- a/packages/react-reconciler/src/work_tags.rs +++ b/packages/react-reconciler/src/work_tags.rs @@ -4,6 +4,7 @@ pub enum WorkTag { HostRoot = 3, HostComponent = 5, HostText = 6, + Fragment = 7, ContextProvider = 8, MemoComponent = 15, } diff --git a/packages/react/src/lib.rs b/packages/react/src/lib.rs index 800d31b..4a30bdf 100644 --- a/packages/react/src/lib.rs +++ b/packages/react/src/lib.rs @@ -171,7 +171,7 @@ pub unsafe fn create_context(default_value: &JsValue) -> JsValue { } #[wasm_bindgen] -pub unsafe fn memo(_type: &JsValue, compare: &JsValue) -> JsValue { +pub fn memo(_type: &JsValue, compare: &JsValue) -> JsValue { let fiber_type = Object::new(); Reflect::set( diff --git a/packages/shared/src/lib.rs b/packages/shared/src/lib.rs index e0c65ab..8e7dee8 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_FRAGMENT_TYPE: &str = "react.fragment"; #[macro_export] macro_rules! log { diff --git a/scripts/build.js b/scripts/build.js index 5d231d0..b552538 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 Fragment +const reactIndexFilename = `${cwd}/dist/react/jsx-dev-runtime.js` +const reactIndexData = fs.readFileSync(reactIndexFilename) +fs.writeFileSync( + reactIndexFilename, + reactIndexData + `export const Fragment='react.fragment';\n` +) +const reactTsIndexFilename = `${cwd}/dist/react/jsx-dev-runtime.d.ts` +const reactTsIndexData = fs.readFileSync(reactTsIndexFilename) +fs.writeFileSync( + reactTsIndexFilename, + reactTsIndexData + `export const Fragment: string;\n` +)