Skip to content
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

Blog 23 - Support Fragment #25

Open
wants to merge 6 commits into
base: feat-update-dom-props
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
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 './memo'
export {default} from './fragment'
19 changes: 19 additions & 0 deletions examples/hello-world/src/fragment/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {useState} from 'react'

export default function App() {
const [num, setNum] = useState(100)
const arr =
num % 2 === 0
? [<li key='1'>1</li>, <li key='2'>2</li>, <li key='3'>3</li>]
: [<li key='3'>3</li>, <li key='2'>2</li>, <li key='1'>1</li>]
return (
<ul onClick={() => setNum((num) => num + 1)}>
{/* <li>4</li>
<li>5</li> */}
{arr}
<span>{num}</span>

{/* {num} */}
</ul>
)
}
7 changes: 7 additions & 0 deletions packages/react-reconciler/src/begin_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<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));
work_in_progress.borrow().child.clone()
}

fn update_memo_component(
work_in_progress: Rc<RefCell<FiberNode>>,
render_lane: Lane,
Expand Down
99 changes: 77 additions & 22 deletions packages/react-reconciler/src/child_fiber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -205,6 +213,26 @@ impl Hash for Key {
}
}

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>>>,
) -> Rc<RefCell<FiberNode>> {
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<RefCell<FiberNode>>,
existing_children: &mut HashMap<Key, Rc<RefCell<FiberNode>>>,
Expand Down Expand Up @@ -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
}

Expand Down
11 changes: 6 additions & 5 deletions packages/react-reconciler/src/commit_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,8 +494,8 @@ fn get_host_parent(fiber: Rc<RefCell<FiberNode>>) -> Option<Rc<RefCell<FiberNode
fn get_host_sibling(fiber: Rc<RefCell<FiberNode>>) -> Option<Rc<dyn Any>> {
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 {
Expand All @@ -515,9 +515,10 @@ fn get_host_sibling(fiber: Rc<RefCell<FiberNode>>) -> Option<Rc<dyn Any>> {
._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;
}
Expand Down
47 changes: 12 additions & 35 deletions packages/react-reconciler/src/fiber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -98,44 +87,28 @@ 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(),
self.flags
)
.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");
}
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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");
Expand Down
13 changes: 2 additions & 11 deletions packages/react-reconciler/src/fiber_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ fn update_state(_: &JsValue) -> Result<Vec<JsValue>, 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<RefCell<Update>>| {
Expand Down Expand Up @@ -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")
Expand Down
6 changes: 3 additions & 3 deletions packages/react-reconciler/src/work_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -198,8 +198,8 @@ fn render_root(root: Rc<RefCell<FiberRootNode>>, 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 {
Expand Down
1 change: 1 addition & 0 deletions packages/react-reconciler/src/work_tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub enum WorkTag {
HostRoot = 3,
HostComponent = 5,
HostText = 6,
Fragment = 7,
ContextProvider = 8,
MemoComponent = 15,
}
2 changes: 1 addition & 1 deletion packages/react/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
14 changes: 14 additions & 0 deletions scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`
)