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 (
+ setNum((num) => num + 1)}>
+ {/* - 4
+ - 5
*/}
+ {arr}
+ {num}
+
+ {/* {num} */}
+
+ )
+}
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`
+)