diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx
index ad1bd41..918688f 100644
--- a/examples/hello-world/src/App.tsx
+++ b/examples/hello-world/src/App.tsx
@@ -1,55 +1 @@
-import {useState, useEffect} from 'react'
-// function App() {
-// const [num, updateNum] = useState(0)
-// const len = 100
-
-// console.log('num', num)
-// return (
-//
{
-// updateNum((num: number) => num + 1)
-// }}>
-// {Array(len)
-// .fill(1)
-// .map((_, i) => {
-// return
-// })}
-//
-// )
-// }
-
-// function Child({i}) {
-// return i am child {i}
-// }
-
-// export default App
-
-const Item = ({i, children}) => {
- for (let i = 0; i < 999999; i++) {}
- return {children}
-}
-
-export default () => {
- const [count, updateCount] = useState(0)
-
- const onClick = () => {
- updateCount(2)
- }
-
- useEffect(() => {
- const button = document.querySelector('button')
- setTimeout(() => updateCount((num) => num + 1), 1000)
- setTimeout(() => button.click(), 1100)
- }, [])
-
- return (
-
-
-
- {Array.from(new Array(1000)).map((v, index) => (
- - {count}
- ))}
-
-
- )
-}
+export {default} from './useCallback'
diff --git a/examples/hello-world/src/ref/index.tsx b/examples/hello-world/src/ref/index.tsx
new file mode 100644
index 0000000..590e2b0
--- /dev/null
+++ b/examples/hello-world/src/ref/index.tsx
@@ -0,0 +1,22 @@
+import {useState, useEffect, useRef} from 'react'
+
+export default function App() {
+ const [isDel, del] = useState(false)
+ const divRef = useRef(null)
+
+ console.warn('render divRef', divRef.current)
+
+ useEffect(() => {
+ console.warn('useEffect divRef', divRef.current)
+ }, [])
+
+ return (
+ del((prev) => !prev)}>
+ {isDel ? null : }
+
+ )
+}
+
+function Child() {
+ return console.warn('dom is:', dom)}>Child
+}
diff --git a/examples/hello-world/src/useCallback/index.tsx b/examples/hello-world/src/useCallback/index.tsx
new file mode 100644
index 0000000..941547e
--- /dev/null
+++ b/examples/hello-world/src/useCallback/index.tsx
@@ -0,0 +1,29 @@
+import {useState, useCallback} from 'react'
+
+let lastFn
+
+export default function App() {
+ const [num, update] = useState(1)
+ console.log('App render ', num)
+
+ const addOne = useCallback(() => update((n) => n + 1), [])
+ // const addOne = () => update((n) => n + 1)
+
+ if (lastFn === addOne) {
+ console.log('useCallback work')
+ }
+
+ lastFn = addOne
+
+ return (
+
+
+ {num}
+
+ )
+}
+
+const Cpn = function ({onClick}) {
+ console.log('Cpn render')
+ return onClick()}>lll
+}
diff --git a/examples/hello-world/src/useMemo/index.tsx b/examples/hello-world/src/useMemo/index.tsx
new file mode 100644
index 0000000..8830014
--- /dev/null
+++ b/examples/hello-world/src/useMemo/index.tsx
@@ -0,0 +1,27 @@
+import {useState, useMemo} from 'react'
+
+let lastCpn
+// 方式1:App提取 bailout四要素
+// 方式2:ExpensiveSubtree用memo包裹
+export default function App() {
+ const [num, update] = useState(0)
+ console.log('App render ', num)
+
+ const Cpn = useMemo(() => , [])
+
+ if (lastCpn === Cpn) {
+ console.log('useMemo work')
+ }
+ lastCpn = Cpn
+ return (
+ update(num + 100)}>
+
num is: {num}
+ {Cpn}
+
+ )
+}
+
+function ExpensiveSubtree() {
+ console.log('ExpensiveSubtree render')
+ return i am child
+}
diff --git a/packages/react-dom/src/host_config.rs b/packages/react-dom/src/host_config.rs
index 48bb999..90b02d8 100644
--- a/packages/react-dom/src/host_config.rs
+++ b/packages/react-dom/src/host_config.rs
@@ -2,11 +2,11 @@ use std::any::Any;
use std::cell::RefCell;
use std::rc::Rc;
-use js_sys::{Function, global, Promise};
use js_sys::JSON::stringify;
-use wasm_bindgen::JsValue;
+use js_sys::{global, Function, Promise};
use wasm_bindgen::prelude::*;
-use web_sys::{Node, window};
+use wasm_bindgen::JsValue;
+use web_sys::{window, Node};
use react_reconciler::HostConfig;
use shared::{log, type_of};
@@ -162,7 +162,14 @@ impl HostConfig for ReactDomHostConfig {
.is_function()
{
let closure_clone = closure.clone();
- queueMicrotask(&closure_clone.borrow_mut().as_ref().unwrap().as_ref().unchecked_ref::());
+ queueMicrotask(
+ &closure_clone
+ .borrow_mut()
+ .as_ref()
+ .unwrap()
+ .as_ref()
+ .unchecked_ref::(),
+ );
closure_clone.borrow_mut().take().unwrap_throw().forget();
} else if js_sys::Reflect::get(&*global(), &JsValue::from_str("Promise"))
.map(|value| value.is_function())
@@ -179,7 +186,15 @@ impl HostConfig for ReactDomHostConfig {
c.forget();
} else {
let closure_clone = closure.clone();
- setTimeout(&closure_clone.borrow_mut().as_ref().unwrap().as_ref().unchecked_ref::(), 0);
+ setTimeout(
+ &closure_clone
+ .borrow_mut()
+ .as_ref()
+ .unwrap()
+ .as_ref()
+ .unchecked_ref::(),
+ 0,
+ );
closure_clone.borrow_mut().take().unwrap_throw().forget();
}
}
diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs
index 8bf815d..d77842b 100644
--- a/packages/react-reconciler/src/begin_work.rs
+++ b/packages/react-reconciler/src/begin_work.rs
@@ -4,9 +4,11 @@ use std::rc::Rc;
use wasm_bindgen::JsValue;
use shared::derive_from_js_value;
+use web_sys::js_sys::Object;
use crate::child_fiber::{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::update_queue::{process_update_queue, ReturnOfProcessUpdateQueue};
@@ -74,6 +76,15 @@ fn update_host_root(
work_in_progress.clone().borrow().child.clone()
}
+fn mark_ref(current: Option>>, work_in_progress: Rc>) {
+ let _ref = { work_in_progress.borrow()._ref.clone() };
+ if (current.is_none() && !_ref.is_null())
+ || (current.is_some() && !Object::is(¤t.as_ref().unwrap().borrow()._ref, &_ref))
+ {
+ work_in_progress.borrow_mut().flags |= Flags::Ref;
+ }
+}
+
fn update_host_component(
work_in_progress: Rc>,
) -> Option>> {
@@ -84,6 +95,9 @@ fn update_host_component(
derive_from_js_value(&ref_fiber_node.pending_props, "children")
};
+ let alternate = { work_in_progress.borrow().alternate.clone() };
+ mark_ref(alternate, work_in_progress.clone());
+
{
reconcile_children(work_in_progress.clone(), Some(next_children));
}
diff --git a/packages/react-reconciler/src/child_fiber.rs b/packages/react-reconciler/src/child_fiber.rs
index 0c653c2..f2df818 100644
--- a/packages/react-reconciler/src/child_fiber.rs
+++ b/packages/react-reconciler/src/child_fiber.rs
@@ -172,7 +172,12 @@ fn reconcile_single_text_node(
current = current_rc.borrow().sibling.clone();
}
- let mut created = FiberNode::new(WorkTag::HostText, props.clone(), JsValue::null());
+ let mut created = FiberNode::new(
+ WorkTag::HostText,
+ props.clone(),
+ JsValue::null(),
+ JsValue::null(),
+ );
created._return = Some(return_fiber.clone());
Rc::new(RefCell::new(created))
}
@@ -235,6 +240,7 @@ fn update_from_map(
WorkTag::HostText,
props.clone(),
JsValue::null(),
+ JsValue::null(),
))))
};
} else if type_of(element, "object") && !element.is_null() {
diff --git a/packages/react-reconciler/src/commit_work.rs b/packages/react-reconciler/src/commit_work.rs
index b5a609d..b3739c8 100644
--- a/packages/react-reconciler/src/commit_work.rs
+++ b/packages/react-reconciler/src/commit_work.rs
@@ -3,461 +3,535 @@ use std::cell::RefCell;
use std::rc::Rc;
use wasm_bindgen::{JsCast, JsValue};
-use web_sys::js_sys::Function;
+use web_sys::js_sys::{Function, Reflect};
-use shared::{derive_from_js_value, log};
+use shared::{derive_from_js_value, log, type_of};
+use web_sys::Node;
use crate::fiber::{FiberNode, FiberRootNode, StateNode};
-use crate::fiber_flags::{Flags, get_mutation_mask, get_passive_mask};
+use crate::fiber_flags::{get_mutation_mask, get_passive_mask, Flags};
use crate::fiber_hooks::Effect;
-use crate::HostConfig;
use crate::work_tags::WorkTag;
use crate::work_tags::WorkTag::{HostComponent, HostRoot, HostText};
+use crate::HOST_CONFIG;
-pub struct CommitWork {
- next_effect: Option>>,
- host_config: Rc,
+static mut NEXT_EFFECT: Option>> = None;
+
+enum Phrase {
+ Mutation,
+ Layout,
}
-impl CommitWork {
- pub fn new(host_config: Rc) -> Self {
- Self {
- next_effect: None,
- host_config,
- }
+fn commit_passive_effect(
+ finished_work: Rc>,
+ root: Rc>,
+ _type: &str,
+) {
+ let finished_work_b = finished_work.borrow();
+ if finished_work_b.tag != WorkTag::FunctionComponent
+ || (_type == "update"
+ && (finished_work_b.flags.clone() & Flags::PassiveEffect == Flags::NoFlags))
+ {
+ return;
}
- fn commit_passive_effect(
- finished_work: Rc>,
- root: Rc>,
- _type: &str,
- ) {
- let finished_work_b = finished_work.borrow();
- if finished_work_b.tag != WorkTag::FunctionComponent
- || (_type == "update"
- && (finished_work_b.flags.clone() & Flags::PassiveEffect == Flags::NoFlags))
- {
- return;
+ let update_queue = &finished_work_b.update_queue;
+ if update_queue.is_some() {
+ let update_queue = update_queue.clone().unwrap();
+ if update_queue.borrow().last_effect.is_none() {
+ log!("When FC has PassiveEffect, the effect should exist.")
}
-
- let update_queue = &finished_work_b.update_queue;
- if update_queue.is_some() {
- let update_queue = update_queue.clone().unwrap();
- if update_queue.borrow().last_effect.is_none() {
- log!("When FC has PassiveEffect, the effect should exist.")
- }
- if _type == "unmount" {
- root.borrow()
- .pending_passive_effects
- .borrow_mut()
- .unmount
- .push(update_queue.borrow().last_effect.clone().unwrap());
- } else {
- root.borrow()
- .pending_passive_effects
- .borrow_mut()
- .update
- .push(update_queue.borrow().last_effect.clone().unwrap());
- }
+ if _type == "unmount" {
+ root.borrow()
+ .pending_passive_effects
+ .borrow_mut()
+ .unmount
+ .push(update_queue.borrow().last_effect.clone().unwrap());
+ } else {
+ root.borrow()
+ .pending_passive_effects
+ .borrow_mut()
+ .update
+ .push(update_queue.borrow().last_effect.clone().unwrap());
}
}
+}
- pub fn commit_hook_effect_list(
- flags: Flags,
- last_effect: Rc>,
- callback: fn(effect: Rc>),
- ) {
- let mut effect = last_effect.borrow().next.clone();
- loop {
- let mut effect_rc = effect.clone().unwrap();
- if effect_rc.borrow().tag.clone() & flags.clone() == flags.clone() {
- callback(effect_rc.clone())
- }
- effect = effect_rc.borrow().next.clone();
- if Rc::ptr_eq(
- &effect.clone().unwrap(),
- last_effect.borrow().next.as_ref().unwrap(),
- ) {
- break;
- }
+pub fn commit_hook_effect_list(
+ flags: Flags,
+ last_effect: Rc>,
+ callback: fn(effect: Rc>),
+) {
+ let mut effect = last_effect.borrow().next.clone();
+ loop {
+ let mut effect_rc = effect.clone().unwrap();
+ if effect_rc.borrow().tag.clone() & flags.clone() == flags.clone() {
+ callback(effect_rc.clone())
+ }
+ effect = effect_rc.borrow().next.clone();
+ if Rc::ptr_eq(
+ &effect.clone().unwrap(),
+ last_effect.borrow().next.as_ref().unwrap(),
+ ) {
+ break;
}
}
- pub fn commit_hook_effect_list_destroy(flags: Flags, last_effect: Rc>) {
- CommitWork::commit_hook_effect_list(flags, last_effect, |effect: Rc>| {
- let destroy = { effect.borrow().destroy.clone() };
- if destroy.is_function() {
- destroy
- .dyn_ref::()
- .unwrap()
- .call0(&JsValue::null());
+}
+pub fn commit_hook_effect_list_destroy(flags: Flags, last_effect: Rc>) {
+ commit_hook_effect_list(flags, last_effect, |effect: Rc>| {
+ let destroy = { effect.borrow().destroy.clone() };
+ if destroy.is_function() {
+ destroy
+ .dyn_ref::()
+ .unwrap()
+ .call0(&JsValue::null());
+ }
+ effect.borrow_mut().tag &= !Flags::HookHasEffect;
+ });
+}
+
+pub fn commit_hook_effect_list_unmount(flags: Flags, last_effect: Rc>) {
+ commit_hook_effect_list(flags, last_effect, |effect: Rc>| {
+ let destroy = &effect.borrow().destroy;
+ if destroy.is_function() {
+ destroy
+ .dyn_ref::()
+ .unwrap()
+ .call0(&JsValue::null());
+ }
+ });
+}
+
+pub fn commit_hook_effect_list_mount(flags: Flags, last_effect: Rc>) {
+ commit_hook_effect_list(flags, last_effect, |effect: Rc>| {
+ let create = { effect.borrow().create.clone() };
+ if create.is_function() {
+ let destroy = create.call0(&JsValue::null()).unwrap();
+ effect.borrow_mut().destroy = destroy;
+ }
+ });
+}
+
+pub fn commit_effects(
+ phrase: Phrase,
+ mask: Flags,
+ callbak: fn(Rc>, Rc>) -> (),
+) -> Box>, Rc>) -> ()> {
+ Box::new(
+ move |finished_work: Rc>, root: Rc>| -> () {
+ unsafe {
+ NEXT_EFFECT = Some(finished_work);
+ while NEXT_EFFECT.is_some() {
+ let next_effect = NEXT_EFFECT.clone().unwrap().clone();
+ let child = next_effect.borrow().child.clone();
+ if child.is_some()
+ && next_effect.borrow().subtree_flags.clone() & mask.clone()
+ != Flags::NoFlags
+ {
+ NEXT_EFFECT = child;
+ } else {
+ while NEXT_EFFECT.is_some() {
+ callbak(NEXT_EFFECT.clone().unwrap(), root.clone());
+ let sibling = NEXT_EFFECT
+ .clone()
+ .clone()
+ .unwrap()
+ .borrow()
+ .sibling
+ .clone();
+ if sibling.is_some() {
+ NEXT_EFFECT = sibling;
+ break;
+ }
+
+ let _return = NEXT_EFFECT
+ .clone()
+ .unwrap()
+ .clone()
+ .borrow()
+ ._return
+ .clone();
+
+ if _return.is_none() {
+ NEXT_EFFECT = None
+ } else {
+ NEXT_EFFECT = _return;
+ }
+ }
+ }
+ }
}
- effect.borrow_mut().tag &= !Flags::HookHasEffect;
- });
+ },
+ )
+}
+
+pub fn commit_layout_effects(
+ finished_work: Rc>,
+ root: Rc>,
+) {
+ commit_effects(
+ Phrase::Layout,
+ Flags::LayoutMask,
+ commit_layout_effects_on_fiber,
+ )(finished_work, root)
+}
+
+pub fn commit_mutation_effects(
+ finished_work: Rc>,
+ root: Rc>,
+) {
+ commit_effects(
+ Phrase::Mutation,
+ get_mutation_mask() | get_passive_mask(),
+ commit_mutation_effects_on_fiber,
+ )(finished_work, root)
+}
+
+fn commit_layout_effects_on_fiber(
+ finished_work: Rc>,
+ root: Rc>,
+) {
+ let flags = finished_work.borrow().flags.clone();
+ let tag = finished_work.borrow().tag.clone();
+ if flags & Flags::Ref != Flags::NoFlags && tag == HostComponent {
+ safely_attach_ref(finished_work.clone());
+ finished_work.borrow_mut().flags -= Flags::Ref;
}
+}
- pub fn commit_hook_effect_list_unmount(flags: Flags, last_effect: Rc>) {
- CommitWork::commit_hook_effect_list(flags, last_effect, |effect: Rc>| {
- let destroy = &effect.borrow().destroy;
- if destroy.is_function() {
- destroy
- .dyn_ref::()
- .unwrap()
- .call0(&JsValue::null());
- }
- });
+fn commit_mutation_effects_on_fiber(
+ finished_work: Rc>,
+ root: Rc>,
+) {
+ let flags = finished_work.borrow().flags.clone();
+ if flags.contains(Flags::Placement) {
+ commit_placement(finished_work.clone());
+ finished_work.borrow_mut().flags -= Flags::Placement;
}
- pub fn commit_hook_effect_list_mount(flags: Flags, last_effect: Rc>) {
- CommitWork::commit_hook_effect_list(flags, last_effect, |effect: Rc>| {
- let create = { effect.borrow().create.clone() };
- if create.is_function() {
- let destroy = create.call0(&JsValue::null()).unwrap();
- effect.borrow_mut().destroy = destroy;
+ if flags.contains(Flags::ChildDeletion) {
+ {
+ let deletions = &finished_work.borrow().deletions;
+ if !deletions.is_empty() {
+ for child_to_delete in deletions {
+ commit_deletion(child_to_delete.clone(), root.clone());
+ }
}
- });
+ }
+
+ finished_work.borrow_mut().flags -= Flags::ChildDeletion;
}
- pub fn commit_mutation_effects(
- &mut self,
- finished_work: Rc>,
- root: Rc>,
- ) {
- self.next_effect = Some(finished_work);
- while self.next_effect.is_some() {
- let next_effect = self.next_effect.clone().unwrap().clone();
- let child = next_effect.borrow().child.clone();
- if child.is_some()
- && next_effect.borrow().subtree_flags.clone()
- & (get_mutation_mask() | get_passive_mask())
- != Flags::NoFlags
- {
- self.next_effect = child;
- } else {
- while self.next_effect.is_some() {
- self.commit_mutation_effects_on_fiber(
- self.next_effect.clone().unwrap(),
- root.clone(),
- );
- let sibling = self
- .next_effect
- .clone()
- .clone()
- .unwrap()
- .borrow()
- .sibling
- .clone();
- if sibling.is_some() {
- self.next_effect = sibling;
- break;
- }
+ if flags.contains(Flags::Update) {
+ commit_update(finished_work.clone());
+ finished_work.borrow_mut().flags -= Flags::Update;
+ }
- let _return = self
- .next_effect
- .clone()
- .unwrap()
- .clone()
- .borrow()
- ._return
- .clone();
+ if flags.clone() & Flags::PassiveEffect != Flags::NoFlags {
+ commit_passive_effect(finished_work.clone(), root, "update");
+ finished_work.borrow_mut().flags -= Flags::PassiveEffect;
+ }
- if _return.is_none() {
- self.next_effect = None
- } else {
- self.next_effect = _return;
- }
- }
- }
- }
+ if flags & Flags::Ref != Flags::NoFlags && finished_work.borrow().tag.clone() == HostComponent {
+ safely_detach_ref(finished_work);
}
+}
- fn commit_mutation_effects_on_fiber(
- &self,
- finished_work: Rc>,
- root: Rc>,
- ) {
- let flags = finished_work.borrow().flags.clone();
- if flags.contains(Flags::Placement) {
- self.commit_placement(finished_work.clone());
- finished_work.borrow_mut().flags -= Flags::Placement;
+fn safely_detach_ref(current: Rc>) {
+ let _ref = current.borrow()._ref.clone();
+ if !_ref.is_null() {
+ if type_of(&_ref, "function") {
+ _ref.dyn_ref::()
+ .unwrap()
+ .call1(&JsValue::null(), &JsValue::null());
+ } else {
+ Reflect::set(&_ref, &"current".into(), &JsValue::null());
}
+ }
+}
- if flags.contains(Flags::ChildDeletion) {
- {
- let deletions = &finished_work.borrow().deletions;
- if !deletions.is_empty() {
- for child_to_delete in deletions {
- self.commit_deletion(child_to_delete.clone(), root.clone());
- }
+fn safely_attach_ref(fiber: Rc>) {
+ let _ref = fiber.borrow()._ref.clone();
+ if !_ref.is_null() {
+ let instance = match fiber.borrow().state_node.clone() {
+ Some(s) => match &*s {
+ StateNode::Element(element) => {
+ let node = (*element).downcast_ref::().unwrap();
+ Some(node.clone())
}
- }
-
- finished_work.borrow_mut().flags -= Flags::ChildDeletion;
- }
+ StateNode::FiberRootNode(_) => None,
+ },
+ None => None,
+ };
- if flags.contains(Flags::Update) {
- self.commit_update(finished_work.clone());
- finished_work.borrow_mut().flags -= Flags::Update;
+ if instance.is_none() {
+ panic!("instance is none")
}
- if flags & Flags::PassiveEffect != Flags::NoFlags {
- CommitWork::commit_passive_effect(finished_work.clone(), root, "update");
- finished_work.borrow_mut().flags -= Flags::PassiveEffect;
+ let instance = instance.as_ref().unwrap();
+ if type_of(&_ref, "function") {
+ _ref.dyn_ref::()
+ .unwrap()
+ .call1(&JsValue::null(), instance);
+ } else {
+ Reflect::set(&_ref, &"current".into(), instance);
}
}
+}
- fn commit_update(&self, finished_work: Rc>) {
- let cloned = finished_work.clone();
- match cloned.borrow().tag {
- WorkTag::HostText => {
- let new_content = derive_from_js_value(&cloned.borrow().pending_props, "content");
- let state_node = FiberNode::derive_state_node(finished_work.clone());
- if let Some(state_node) = state_node.clone() {
- self.host_config
+fn commit_update(finished_work: Rc>) {
+ let cloned = finished_work.clone();
+ match cloned.borrow().tag {
+ WorkTag::HostText => {
+ let new_content = derive_from_js_value(&cloned.borrow().pending_props, "content");
+ let state_node = FiberNode::derive_state_node(finished_work.clone());
+ if let Some(state_node) = state_node.clone() {
+ unsafe {
+ HOST_CONFIG
+ .as_ref()
+ .unwrap()
.commit_text_update(state_node.clone(), &new_content)
}
}
- _ => log!("commit_update, unsupported type"),
- };
- }
+ }
+ _ => log!("commit_update, unsupported type"),
+ };
+}
- fn commit_deletion(
- &self,
- child_to_delete: Rc>,
- root: Rc>,
- ) {
- let first_host_fiber: Rc>>>> =
- Rc::new(RefCell::new(None));
- self.commit_nested_unmounts(child_to_delete.clone(), |unmount_fiber| {
- let cloned = first_host_fiber.clone();
- match unmount_fiber.borrow().tag {
- WorkTag::FunctionComponent => {
- CommitWork::commit_passive_effect(
- unmount_fiber.clone(),
- root.clone(),
- "unmount",
- );
- }
- WorkTag::HostRoot => {}
- WorkTag::HostComponent => {
- if cloned.borrow().is_none() {
- *cloned.borrow_mut() = Some(unmount_fiber.clone());
- }
+fn commit_deletion(child_to_delete: Rc>, root: Rc>) {
+ let first_host_fiber: Rc>>>> = Rc::new(RefCell::new(None));
+ commit_nested_unmounts(child_to_delete.clone(), |unmount_fiber| {
+ let cloned = first_host_fiber.clone();
+ match unmount_fiber.borrow().tag {
+ WorkTag::FunctionComponent => {
+ commit_passive_effect(unmount_fiber.clone(), root.clone(), "unmount");
+ }
+ WorkTag::HostRoot => {}
+ WorkTag::HostComponent => {
+ if cloned.borrow().is_none() {
+ *cloned.borrow_mut() = Some(unmount_fiber.clone());
}
- WorkTag::HostText => {
- if cloned.borrow().is_none() {
- *cloned.borrow_mut() = Some(unmount_fiber.clone());
- }
+ }
+ WorkTag::HostText => {
+ if cloned.borrow().is_none() {
+ *cloned.borrow_mut() = Some(unmount_fiber.clone());
}
- };
- });
-
- let first_host_fiber = first_host_fiber.clone();
- if first_host_fiber.borrow().is_some() {
- let host_parent_state_node = FiberNode::derive_state_node(
- self.get_host_parent(child_to_delete.clone()).unwrap(),
- );
- let first_host_fiber_state_node =
- FiberNode::derive_state_node((*first_host_fiber.borrow()).clone().unwrap());
- self.host_config.remove_child(
+ }
+ };
+ });
+
+ let first_host_fiber = first_host_fiber.clone();
+ if first_host_fiber.borrow().is_some() {
+ let host_parent_state_node =
+ FiberNode::derive_state_node(get_host_parent(child_to_delete.clone()).unwrap());
+ let first_host_fiber_state_node =
+ FiberNode::derive_state_node((*first_host_fiber.borrow()).clone().unwrap());
+ unsafe {
+ HOST_CONFIG.as_ref().unwrap().remove_child(
first_host_fiber_state_node.unwrap(),
host_parent_state_node.unwrap(),
)
}
-
- child_to_delete.clone().borrow_mut()._return = None;
- child_to_delete.clone().borrow_mut().child = None;
}
- fn commit_nested_unmounts(&self, root: Rc>, on_commit_unmount: F)
- where
- F: Fn(Rc>),
- {
- let mut node = root.clone();
- loop {
- on_commit_unmount(node.clone());
+ child_to_delete.clone().borrow_mut()._return = None;
+ child_to_delete.clone().borrow_mut().child = None;
+}
- let node_cloned = node.clone();
- if node_cloned.borrow().child.is_some() {
- node_cloned
- .borrow_mut()
- .child
- .clone()
- .unwrap()
- .clone()
- .borrow_mut()
- ._return = Some(node.clone());
- node = node_cloned.borrow().child.clone().unwrap();
- continue;
- }
- if Rc::ptr_eq(&node, &root.clone()) {
- return;
- }
- while node.clone().borrow().sibling.is_none() {
- if node.clone().borrow()._return.is_none()
- || Rc::ptr_eq(node.clone().borrow()._return.as_ref().unwrap(), &root)
- {
- return;
- }
- node = node.clone().borrow()._return.clone().unwrap();
- }
+fn commit_nested_unmounts(root: Rc>, on_commit_unmount: F)
+where
+ F: Fn(Rc>),
+{
+ let mut node = root.clone();
+ loop {
+ on_commit_unmount(node.clone());
- let node_cloned = node.clone();
- let _return = { node_cloned.borrow()._return.clone() };
+ let node_cloned = node.clone();
+ if node_cloned.borrow().child.is_some() {
node_cloned
.borrow_mut()
- .sibling
+ .child
.clone()
.unwrap()
.clone()
.borrow_mut()
- ._return = _return;
- node = node_cloned.borrow().sibling.clone().unwrap();
+ ._return = Some(node.clone());
+ node = node_cloned.borrow().child.clone().unwrap();
+ continue;
}
- }
-
- fn commit_placement(&self, finished_work: Rc>) {
- let host_parent = self.get_host_parent(finished_work.clone());
- if host_parent.is_none() {
+ if Rc::ptr_eq(&node, &root.clone()) {
return;
}
- let parent_state_node = FiberNode::derive_state_node(host_parent.unwrap());
- let sibling = self.get_host_sibling(finished_work.clone());
-
- if parent_state_node.is_some() {
- self.insert_or_append_placement_node_into_container(
- finished_work.clone(),
- parent_state_node.unwrap(),
- sibling,
- );
+ while node.clone().borrow().sibling.is_none() {
+ if node.clone().borrow()._return.is_none()
+ || Rc::ptr_eq(node.clone().borrow()._return.as_ref().unwrap(), &root)
+ {
+ return;
+ }
+ node = node.clone().borrow()._return.clone().unwrap();
}
+
+ let node_cloned = node.clone();
+ let _return = { node_cloned.borrow()._return.clone() };
+ node_cloned
+ .borrow_mut()
+ .sibling
+ .clone()
+ .unwrap()
+ .clone()
+ .borrow_mut()
+ ._return = _return;
+ node = node_cloned.borrow().sibling.clone().unwrap();
}
+}
- fn get_element_from_state_node(&self, state_node: Rc) -> Rc {
- match &*state_node {
- StateNode::FiberRootNode(root) => root.clone().borrow().container.clone(),
- StateNode::Element(ele) => ele.clone(),
- }
+fn commit_placement(finished_work: Rc>) {
+ let host_parent = get_host_parent(finished_work.clone());
+ if host_parent.is_none() {
+ return;
+ }
+ let parent_state_node = FiberNode::derive_state_node(host_parent.unwrap());
+ let sibling = get_host_sibling(finished_work.clone());
+
+ if parent_state_node.is_some() {
+ insert_or_append_placement_node_into_container(
+ finished_work.clone(),
+ parent_state_node.unwrap(),
+ sibling,
+ );
+ }
+}
+
+fn get_element_from_state_node(state_node: Rc) -> Rc {
+ match &*state_node {
+ StateNode::FiberRootNode(root) => root.clone().borrow().container.clone(),
+ StateNode::Element(ele) => ele.clone(),
}
+}
- fn insert_or_append_placement_node_into_container(
- &self,
- fiber: Rc>,
- parent: Rc,
- before: Option>,
- ) {
- let fiber = fiber.clone();
- let tag = fiber.borrow().tag.clone();
- if tag == WorkTag::HostComponent || tag == WorkTag::HostText {
- let state_node = fiber.clone().borrow().state_node.clone().unwrap();
- let state_node = self.get_element_from_state_node(state_node);
-
- if before.is_some() {
- self.host_config.insert_child_to_container(
+fn insert_or_append_placement_node_into_container(
+ fiber: Rc>,
+ parent: Rc,
+ before: Option>,
+) {
+ let fiber = fiber.clone();
+ let tag = fiber.borrow().tag.clone();
+ if tag == WorkTag::HostComponent || tag == WorkTag::HostText {
+ let state_node = fiber.clone().borrow().state_node.clone().unwrap();
+ let state_node = get_element_from_state_node(state_node);
+
+ if before.is_some() {
+ unsafe {
+ HOST_CONFIG.as_ref().unwrap().insert_child_to_container(
state_node,
parent,
before.clone().unwrap(),
- );
- } else {
- self.host_config
- .append_child_to_container(state_node, parent.clone());
- }
-
- return;
+ )
+ };
+ } else {
+ unsafe {
+ HOST_CONFIG
+ .as_ref()
+ .unwrap()
+ .append_child_to_container(state_node, parent.clone())
+ };
}
- let child = fiber.borrow().child.clone();
- if child.is_some() {
- self.insert_or_append_placement_node_into_container(
- child.clone().unwrap(),
+ return;
+ }
+
+ let child = fiber.borrow().child.clone();
+ if child.is_some() {
+ insert_or_append_placement_node_into_container(
+ child.clone().unwrap(),
+ parent.clone(),
+ before.clone(),
+ );
+ let mut sibling = child.unwrap().clone().borrow().sibling.clone();
+ while sibling.is_some() {
+ insert_or_append_placement_node_into_container(
+ sibling.clone().unwrap(),
parent.clone(),
before.clone(),
);
- let mut sibling = child.unwrap().clone().borrow().sibling.clone();
- while sibling.is_some() {
- self.insert_or_append_placement_node_into_container(
- sibling.clone().unwrap(),
- parent.clone(),
- before.clone(),
- );
- sibling = sibling.clone().unwrap().clone().borrow().sibling.clone();
- }
+ sibling = sibling.clone().unwrap().clone().borrow().sibling.clone();
}
}
+}
- fn get_host_parent(&self, fiber: Rc>) -> Option>> {
- let mut parent = fiber.clone().borrow()._return.clone();
- while parent.is_some() {
- let p = parent.clone().unwrap();
- let parent_tag = p.borrow().tag.clone();
- if parent_tag == WorkTag::HostComponent || parent_tag == WorkTag::HostRoot {
- return Some(p);
- }
- parent = p.borrow()._return.clone();
+fn get_host_parent(fiber: Rc>) -> Option>> {
+ let mut parent = fiber.clone().borrow()._return.clone();
+ while parent.is_some() {
+ let p = parent.clone().unwrap();
+ let parent_tag = p.borrow().tag.clone();
+ if parent_tag == WorkTag::HostComponent || parent_tag == WorkTag::HostRoot {
+ return Some(p);
}
-
- None
+ parent = p.borrow()._return.clone();
}
- /**
- * 难点在于目标fiber的hostSibling可能并不是他的同级sibling
- * 比如: 其中:function B() {return } 所以A的hostSibling实际是B的child
- * 实际情况层级可能更深
- * 同时:一个fiber被标记Placement,那他就是不稳定的(他对应的DOM在本次commit阶段会移动),也不能作为hostSibling
- */
- fn get_host_sibling(&self, fiber: Rc>) -> Option> {
- let mut node = Some(fiber);
- 'find_sibling: loop {
- let node_rc = node.clone().unwrap();
- while node_rc.borrow().sibling.is_none() {
- let parent = node_rc.borrow()._return.clone();
- let tag = parent.clone().unwrap().borrow().tag.clone();
- if parent.is_none() || tag == HostComponent || tag == HostRoot {
- return None;
- }
- node = parent.clone();
+ None
+}
+
+/**
+ * 难点在于目标fiber的hostSibling可能并不是他的同级sibling
+ * 比如: 其中:function B() {return } 所以A的hostSibling实际是B的child
+ * 实际情况层级可能更深
+ * 同时:一个fiber被标记Placement,那他就是不稳定的(他对应的DOM在本次commit阶段会移动),也不能作为hostSibling
+ */
+fn get_host_sibling(fiber: Rc>) -> Option> {
+ let mut node = Some(fiber);
+ 'find_sibling: loop {
+ let node_rc = node.clone().unwrap();
+ while node_rc.borrow().sibling.is_none() {
+ let parent = node_rc.borrow()._return.clone();
+ let tag = parent.clone().unwrap().borrow().tag.clone();
+ if parent.is_none() || tag == HostComponent || tag == HostRoot {
+ return None;
}
+ node = parent.clone();
+ }
- let node_rc = node.clone().unwrap();
- let _return = { node_rc.borrow()._return.clone() };
- node_rc
- .borrow_mut()
- .sibling
- .clone()
- .unwrap()
- .borrow_mut()
- ._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 {
- if node_rc.borrow().flags.contains(Flags::Placement) {
- continue 'find_sibling;
- }
- if node_rc.borrow().child.is_none() {
- continue 'find_sibling;
- } else {
- node_rc
- .borrow_mut()
- .child
- .clone()
- .unwrap()
- .borrow_mut()
- ._return = node.clone();
- node = node_rc.borrow().child.clone();
- }
+ let node_rc = node.clone().unwrap();
+ let _return = { node_rc.borrow()._return.clone() };
+ node_rc
+ .borrow_mut()
+ .sibling
+ .clone()
+ .unwrap()
+ .borrow_mut()
+ ._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 {
+ if node_rc.borrow().flags.contains(Flags::Placement) {
+ continue 'find_sibling;
}
- if !node
- .clone()
- .unwrap()
- .borrow()
- .flags
- .contains(Flags::Placement)
- {
- return Some(self.get_element_from_state_node(
- node.clone().unwrap().borrow().state_node.clone().unwrap(),
- ));
+ if node_rc.borrow().child.is_none() {
+ continue 'find_sibling;
+ } else {
+ node_rc
+ .borrow_mut()
+ .child
+ .clone()
+ .unwrap()
+ .borrow_mut()
+ ._return = node.clone();
+ node = node_rc.borrow().child.clone();
}
}
+ if !node
+ .clone()
+ .unwrap()
+ .borrow()
+ .flags
+ .contains(Flags::Placement)
+ {
+ return Some(get_element_from_state_node(
+ node.clone().unwrap().borrow().state_node.clone().unwrap(),
+ ));
+ }
}
}
diff --git a/packages/react-reconciler/src/complete_work.rs b/packages/react-reconciler/src/complete_work.rs
index 455a3bb..fdfb179 100644
--- a/packages/react-reconciler/src/complete_work.rs
+++ b/packages/react-reconciler/src/complete_work.rs
@@ -9,13 +9,17 @@ use shared::{derive_from_js_value, log};
use crate::fiber::{FiberNode, StateNode};
use crate::fiber_flags::Flags;
-use crate::HostConfig;
use crate::work_tags::WorkTag;
+use crate::HostConfig;
pub struct CompleteWork {
pub host_config: Rc,
}
+fn mark_ref(fiber: Rc>) {
+ fiber.borrow_mut().flags |= Flags::Ref;
+}
+
impl CompleteWork {
pub(crate) fn new(host_config: Rc) -> Self {
Self { host_config }
@@ -65,9 +69,9 @@ impl CompleteWork {
let node_cloned = node.clone().unwrap().clone();
if node_cloned.borrow()._return.is_none()
|| Rc::ptr_eq(
- &node_cloned.borrow()._return.as_ref().unwrap(),
- &work_in_progress,
- )
+ &node_cloned.borrow()._return.as_ref().unwrap(),
+ &work_in_progress,
+ )
{
return;
}
@@ -135,7 +139,14 @@ impl CompleteWork {
}
WorkTag::HostComponent => {
if current.is_some() && work_in_progress_cloned.borrow().state_node.is_some() {
- log!("update properties")
+ log!("update properties");
+ let current = current.unwrap();
+ if !Object::is(
+ ¤t.borrow()._ref,
+ &work_in_progress_cloned.borrow()._ref,
+ ) {
+ mark_ref(work_in_progress.clone());
+ }
} else {
let instance = self.host_config.create_instance(
work_in_progress
@@ -150,6 +161,9 @@ impl CompleteWork {
self.append_all_children(instance.clone(), work_in_progress.clone());
work_in_progress.clone().borrow_mut().state_node =
Some(Rc::new(StateNode::Element(instance.clone())));
+ if !work_in_progress.borrow()._ref.is_null() {
+ mark_ref(work_in_progress.clone());
+ }
}
self.bubble_properties(work_in_progress.clone());
diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs
index 9c801ec..f02cae9 100644
--- a/packages/react-reconciler/src/fiber.rs
+++ b/packages/react-reconciler/src/fiber.rs
@@ -58,6 +58,7 @@ pub struct FiberNode {
pub child: Option>>,
pub alternate: Option>>,
pub _type: JsValue,
+ pub _ref: JsValue,
pub flags: Flags,
pub subtree_flags: Flags,
pub memoized_props: JsValue,
@@ -111,7 +112,7 @@ impl Debug for FiberNode {
}
impl FiberNode {
- pub fn new(tag: WorkTag, pending_props: JsValue, key: JsValue) -> Self {
+ pub fn new(tag: WorkTag, pending_props: JsValue, key: JsValue, _ref: JsValue) -> Self {
Self {
index: 0,
tag,
@@ -130,6 +131,7 @@ impl FiberNode {
subtree_flags: Flags::NoFlags,
deletions: vec![],
lanes: Lane::NoLane,
+ _ref,
}
}
@@ -137,6 +139,7 @@ impl FiberNode {
let _type = derive_from_js_value(ele, "type");
let key = derive_from_js_value(ele, "key");
let props = derive_from_js_value(ele, "props");
+ let _ref = derive_from_js_value(ele, "ref");
let mut fiber_tag = WorkTag::FunctionComponent;
if _type.is_string() {
@@ -145,7 +148,7 @@ impl FiberNode {
log!("Unsupported type {:?}", ele);
}
- let mut fiber = FiberNode::new(fiber_tag, props, key);
+ let mut fiber = FiberNode::new(fiber_tag, props, key, _ref);
fiber._type = _type;
fiber
}
@@ -175,7 +178,8 @@ impl FiberNode {
return if w.is_none() {
let wip = {
let c = c_rc.borrow();
- let mut wip = FiberNode::new(c.tag.clone(), pending_props, c.key.clone());
+ let mut wip =
+ FiberNode::new(c.tag.clone(), pending_props, c.key.clone(), c._ref.clone());
wip._type = c._type.clone();
wip.state_node = c.state_node.clone();
@@ -210,6 +214,7 @@ impl FiberNode {
wip.child = c.child.clone();
wip.memoized_props = c.memoized_props.clone();
wip.memoized_state = c.memoized_state.clone();
+ wip._ref = c._ref.clone();
}
w.clone()
};
diff --git a/packages/react-reconciler/src/fiber_flags.rs b/packages/react-reconciler/src/fiber_flags.rs
index ac3d87b..07cc8e6 100644
--- a/packages/react-reconciler/src/fiber_flags.rs
+++ b/packages/react-reconciler/src/fiber_flags.rs
@@ -8,6 +8,8 @@ bitflags! {
const Update = 0b00000100;
const ChildDeletion = 0b00010000;
const PassiveEffect = 0b00100000;
+ const Ref = 0b01000000;
+ const LayoutMask = 0b01000000; // Ref
// effect hook
const HookHasEffect = 0b00100001;
const Passive = 0b00000010;
@@ -24,4 +26,6 @@ pub fn get_mutation_mask() -> Flags {
Flags::Placement | Flags::Update | Flags::ChildDeletion
}
-pub fn get_passive_mask() -> Flags { Flags::PassiveEffect | Flags::ChildDeletion }
+pub fn get_passive_mask() -> Flags {
+ Flags::PassiveEffect | Flags::ChildDeletion
+}
diff --git a/packages/react-reconciler/src/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs
index b916b51..3e041ec 100644
--- a/packages/react-reconciler/src/fiber_hooks.rs
+++ b/packages/react-reconciler/src/fiber_hooks.rs
@@ -108,8 +108,40 @@ fn update_hooks_to_dispatcher(is_update: bool) {
.clone();
use_effect_closure.forget();
+ // use_ref
+ let use_ref_closure = Closure::wrap(Box::new(if is_update { update_ref } else { mount_ref })
+ as Box JsValue>);
+ let use_ref = use_ref_closure.as_ref().unchecked_ref::().clone();
+ use_ref_closure.forget();
+
+ // use_memo
+ let use_memo_closure =
+ Closure::wrap(Box::new(if is_update { update_memo } else { mount_memo })
+ as Box Result>);
+ let use_memo = use_memo_closure
+ .as_ref()
+ .unchecked_ref::()
+ .clone();
+ use_memo_closure.forget();
+
+ // use_callback
+ let use_callback_clusure = Closure::wrap(Box::new(if is_update {
+ update_callback
+ } else {
+ mount_callback
+ }) as Box JsValue>);
+ let use_callback = use_callback_clusure
+ .as_ref()
+ .unchecked_ref::()
+ .clone();
+ use_callback_clusure.forget();
+
Reflect::set(&object, &"use_state".into(), &use_state).expect("TODO: panic set use_state");
Reflect::set(&object, &"use_effect".into(), &use_effect).expect("TODO: panic set use_effect");
+ Reflect::set(&object, &"use_ref".into(), &use_ref).expect("TODO: panic set use_ref");
+ Reflect::set(&object, &"use_memo".into(), &use_memo).expect("TODO: panic set use_memo");
+ Reflect::set(&object, &"use_callback".into(), &use_callback)
+ .expect("TODO: panic set use_callback");
updateDispatcher(&object.into());
}
@@ -283,6 +315,8 @@ fn mount_state(initial_state: &JsValue) -> Result, JsValue> {
}
hook.as_ref().unwrap().clone().borrow_mut().memoized_state =
Some(MemoizedState::MemoizedJsValue(memoized_state.clone()));
+ hook.as_ref().unwrap().clone().borrow_mut().base_state =
+ Some(MemoizedState::MemoizedJsValue(memoized_state.clone()));
unsafe {
if CURRENTLY_RENDERING_FIBER.is_none() {
@@ -529,3 +563,118 @@ fn are_hook_inputs_equal(next_deps: &JsValue, pre_deps: &JsValue) -> bool {
}
return true;
}
+
+fn mount_ref(initial_value: &JsValue) -> JsValue {
+ let hook = mount_work_in_progress_hook();
+ let ref_obj: Object = Object::new();
+ Reflect::set(&ref_obj, &"current".into(), initial_value);
+ hook.as_ref().unwrap().borrow_mut().memoized_state =
+ Some(MemoizedState::MemoizedJsValue(ref_obj.clone().into()));
+ ref_obj.into()
+}
+
+fn update_ref(initial_value: &JsValue) -> JsValue {
+ let hook = update_work_in_progress_hook();
+ match hook.unwrap().borrow_mut().memoized_state.clone() {
+ Some(MemoizedState::MemoizedJsValue(value)) => value,
+ _ => panic!("ref is none"),
+ }
+}
+
+fn mount_memo(create: Function, deps: JsValue) -> Result {
+ let hook = mount_work_in_progress_hook();
+ let next_deps = if deps.is_undefined() {
+ JsValue::null()
+ } else {
+ deps
+ };
+ let next_value = create.call0(&JsValue::null())?;
+ let array = Array::new();
+ array.push(&next_value);
+ array.push(&next_deps);
+ hook.as_ref().unwrap().clone().borrow_mut().memoized_state =
+ Some(MemoizedState::MemoizedJsValue(array.into()));
+ Ok(next_value)
+}
+
+fn update_memo(create: Function, deps: JsValue) -> Result {
+ let hook = update_work_in_progress_hook();
+ let next_deps = if deps.is_undefined() {
+ JsValue::null()
+ } else {
+ deps
+ };
+
+ if let MemoizedState::MemoizedJsValue(prev_state) = hook
+ .clone()
+ .unwrap()
+ .borrow()
+ .memoized_state
+ .as_ref()
+ .unwrap()
+ {
+ if !next_deps.is_null() {
+ let arr = prev_state.dyn_ref::().unwrap();
+ let prev_deps = arr.get(1);
+ if are_hook_inputs_equal(&next_deps, &prev_deps) {
+ return Ok(arr.get(0));
+ }
+ }
+ let next_value = create.call0(&JsValue::null())?;
+ let array = Array::new();
+ array.push(&next_value);
+ array.push(&next_deps);
+ hook.as_ref().unwrap().clone().borrow_mut().memoized_state =
+ Some(MemoizedState::MemoizedJsValue(array.into()));
+ return Ok(next_value);
+ }
+ panic!("update_memo, memoized_state is not JsValue");
+}
+
+fn mount_callback(callback: Function, deps: JsValue) -> JsValue {
+ let hook = mount_work_in_progress_hook();
+ let next_deps = if deps.is_undefined() {
+ JsValue::null()
+ } else {
+ deps
+ };
+ let array = Array::new();
+ array.push(&callback);
+ array.push(&next_deps);
+ hook.as_ref().unwrap().clone().borrow_mut().memoized_state =
+ Some(MemoizedState::MemoizedJsValue(array.into()));
+ callback.into()
+}
+
+fn update_callback(callback: Function, deps: JsValue) -> JsValue {
+ let hook = update_work_in_progress_hook();
+ let next_deps = if deps.is_undefined() {
+ JsValue::null()
+ } else {
+ deps
+ };
+
+ if let MemoizedState::MemoizedJsValue(prev_state) = hook
+ .clone()
+ .unwrap()
+ .borrow()
+ .memoized_state
+ .as_ref()
+ .unwrap()
+ {
+ if !next_deps.is_null() {
+ let arr = prev_state.dyn_ref::().unwrap();
+ let prev_deps = arr.get(1);
+ if are_hook_inputs_equal(&next_deps, &prev_deps) {
+ return arr.get(0);
+ }
+ }
+ let array = Array::new();
+ array.push(&callback);
+ array.push(&next_deps);
+ hook.as_ref().unwrap().clone().borrow_mut().memoized_state =
+ Some(MemoizedState::MemoizedJsValue(array.into()));
+ return callback.into();
+ }
+ panic!("update_callback, memoized_state is not JsValue");
+}
diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs
index ee5e21b..454510a 100644
--- a/packages/react-reconciler/src/lib.rs
+++ b/packages/react-reconciler/src/lib.rs
@@ -4,7 +4,6 @@ use std::rc::Rc;
use wasm_bindgen::JsValue;
-use crate::commit_work::CommitWork;
use crate::complete_work::CompleteWork;
use crate::fiber::{FiberNode, FiberRootNode, StateNode};
// use crate::fiber_hooks::{WORK_LOOP as Fiber_HOOKS};
@@ -29,7 +28,6 @@ mod work_tags;
pub static mut HOST_CONFIG: Option> = None;
static mut COMPLETE_WORK: Option = None;
-static mut COMMIT_WORK: Option = None;
pub trait HostConfig {
fn create_text_instance(&self, content: &JsValue) -> Rc;
@@ -60,6 +58,7 @@ impl Reconciler {
WorkTag::HostRoot,
JsValue::null(),
JsValue::null(),
+ JsValue::null(),
)));
host_root_fiber.clone().borrow_mut().update_queue = Some(create_update_queue());
let root = Rc::new(RefCell::new(FiberRootNode::new(
@@ -82,7 +81,6 @@ impl Reconciler {
unsafe {
HOST_CONFIG = Some(self.host_config.clone());
COMPLETE_WORK = Some(CompleteWork::new(self.host_config.clone()));
- COMMIT_WORK = Some(CommitWork::new(self.host_config.clone()));
schedule_update_on_fiber(host_root_fiber, root_render_priority);
}
element.clone()
diff --git a/packages/react-reconciler/src/work_loop.rs b/packages/react-reconciler/src/work_loop.rs
index 16524b9..40bcab6 100644
--- a/packages/react-reconciler/src/work_loop.rs
+++ b/packages/react-reconciler/src/work_loop.rs
@@ -13,13 +13,16 @@ use scheduler::{
use shared::{is_dev, log};
use crate::begin_work::begin_work;
-use crate::commit_work::CommitWork;
+use crate::commit_work::{
+ commit_hook_effect_list_destroy, commit_hook_effect_list_mount,
+ commit_hook_effect_list_unmount, commit_layout_effects, commit_mutation_effects,
+};
use crate::fiber::{FiberNode, FiberRootNode, PendingPassiveEffects, StateNode};
use crate::fiber_flags::{get_mutation_mask, get_passive_mask, Flags};
use crate::fiber_lanes::{get_highest_priority, lanes_to_scheduler_priority, merge_lanes, Lane};
use crate::sync_task_queue::{flush_sync_callbacks, schedule_sync_callback};
use crate::work_tags::WorkTag;
-use crate::{COMMIT_WORK, COMPLETE_WORK, HOST_CONFIG};
+use crate::{COMPLETE_WORK, HOST_CONFIG};
bitflags! {
#[derive(Debug, Clone)]
@@ -325,23 +328,17 @@ fn flush_passive_effects(pending_passive_effects: Rc>) {
EXECUTION_CONTEXT |= ExecutionContext::CommitContext;
}
- unsafe {
- COMMIT_WORK
- .as_mut()
- .unwrap()
- .commit_mutation_effects(finished_work.clone(), root.clone());
- }
+ // effect
+ // 1/3: Before Mutation
+
+ // 2/3: Mutation
+ commit_mutation_effects(finished_work.clone(), root.clone());
+
+ // Switch Fiber Tree
cloned.borrow_mut().current = finished_work.clone();
+ // 3/3: Layout
+ commit_layout_effects(finished_work.clone(), root.clone());
+
unsafe {
EXECUTION_CONTEXT = prev_execution_context;
}
diff --git a/packages/react/src/current_dispatcher.rs b/packages/react/src/current_dispatcher.rs
index d7a79fd..9b63a0c 100644
--- a/packages/react/src/current_dispatcher.rs
+++ b/packages/react/src/current_dispatcher.rs
@@ -1,21 +1,34 @@
use js_sys::{Function, Reflect};
-use wasm_bindgen::JsValue;
use wasm_bindgen::prelude::*;
+use wasm_bindgen::JsValue;
+
+use crate::use_callback;
#[derive(Debug)]
pub struct Dispatcher {
pub use_state: Function,
pub use_effect: Function,
- // pub use_callback: *const dyn Fn(),
+ pub use_ref: Function,
+ pub use_memo: Function,
+ pub use_callback: Function,
}
unsafe impl Send for Dispatcher {}
impl Dispatcher {
- pub fn new(use_state: Function, use_effect: Function) -> Self {
+ pub fn new(
+ use_state: Function,
+ use_effect: Function,
+ use_ref: Function,
+ use_memo: Function,
+ use_callback: Function,
+ ) -> Self {
Dispatcher {
use_state,
use_effect,
+ use_ref,
+ use_memo,
+ use_callback,
}
}
}
@@ -37,5 +50,14 @@ fn derive_function_from_js_value(js_value: &JsValue, name: &str) -> Function {
pub unsafe fn update_dispatcher(args: &JsValue) {
let use_state = derive_function_from_js_value(args, "use_state");
let use_effect = derive_function_from_js_value(args, "use_effect");
- CURRENT_DISPATCHER.current = Some(Box::new(Dispatcher::new(use_state, use_effect)))
+ let use_ref = derive_function_from_js_value(args, "use_ref");
+ let use_memo = derive_function_from_js_value(args, "use_memo");
+ let use_callback = derive_function_from_js_value(args, "use_callback");
+ CURRENT_DISPATCHER.current = Some(Box::new(Dispatcher::new(
+ use_state,
+ use_effect,
+ use_ref,
+ use_memo,
+ use_callback,
+ )))
}
diff --git a/packages/react/src/lib.rs b/packages/react/src/lib.rs
index 6d0f80a..7b02db8 100644
--- a/packages/react/src/lib.rs
+++ b/packages/react/src/lib.rs
@@ -1,4 +1,4 @@
-use js_sys::{Array, JSON, Object, Reflect};
+use js_sys::{Array, Object, Reflect, JSON};
use wasm_bindgen::prelude::*;
use shared::{derive_from_js_value, REACT_ELEMENT_TYPE};
@@ -35,7 +35,7 @@ pub fn jsx_dev(_type: &JsValue, config: &JsValue, key: &JsValue) -> JsValue {
&"$$typeof".into(),
&JsValue::from_str(REACT_ELEMENT_TYPE),
)
- .expect("$$typeof panic");
+ .expect("$$typeof panic");
Reflect::set(&react_element, &"type".into(), _type).expect("type panic");
let props = Object::new();
@@ -90,7 +90,8 @@ pub fn jsx(_type: &JsValue, config: &JsValue, maybe_children: &JsValue) -> JsVal
Reflect::set(&config, &"children".into(), &children.get(0))
.expect("TODO: panic children");
} else {
- Reflect::set(&config, &"children".into(), maybe_children).expect("TODO: panic set children");
+ Reflect::set(&config, &"children".into(), maybe_children)
+ .expect("TODO: panic set children");
}
}
}
@@ -103,11 +104,11 @@ pub fn is_valid_element(object: &JsValue) -> bool {
object.is_object()
&& !object.is_null()
&& Reflect::get(&object, &"$$typeof".into())
- .unwrap_or("".into())
- .as_string()
- .unwrap_or("".into())
- .as_str()
- == REACT_ELEMENT_TYPE
+ .unwrap_or("".into())
+ .as_string()
+ .unwrap_or("".into())
+ .as_str()
+ == REACT_ELEMENT_TYPE
}
#[wasm_bindgen(js_name = useState)]
@@ -121,3 +122,21 @@ pub unsafe fn use_effect(create: &JsValue, deps: &JsValue) {
let use_effect = &CURRENT_DISPATCHER.current.as_ref().unwrap().use_effect;
use_effect.call2(&JsValue::null(), create, deps);
}
+
+#[wasm_bindgen(js_name = useRef)]
+pub unsafe fn use_ref(initial_value: &JsValue) -> Result {
+ let use_ref = &CURRENT_DISPATCHER.current.as_ref().unwrap().use_ref;
+ use_ref.call1(&JsValue::null(), initial_value)
+}
+
+#[wasm_bindgen(js_name = useMemo)]
+pub unsafe fn use_memo(create: &JsValue, deps: &JsValue) -> Result {
+ let use_memo = &CURRENT_DISPATCHER.current.as_ref().unwrap().use_memo;
+ use_memo.call2(&JsValue::null(), create, deps)
+}
+
+#[wasm_bindgen(js_name = useCallback)]
+pub unsafe fn use_callback(callback: &JsValue, deps: &JsValue) -> Result {
+ let use_callback = &CURRENT_DISPATCHER.current.as_ref().unwrap().use_callback;
+ use_callback.call2(&JsValue::null(), callback, deps)
+}
diff --git a/packages/scheduler/src/lib.rs b/packages/scheduler/src/lib.rs
index bbd5020..fbc551d 100644
--- a/packages/scheduler/src/lib.rs
+++ b/packages/scheduler/src/lib.rs
@@ -288,7 +288,6 @@ fn flush_work(has_time_remaining: bool, initial_time: f64) -> bool {
pub fn unstable_should_yield_to_host() -> bool {
unsafe {
let time_elapsed = unstable_now() - START_TIME;
- log!("start_time: {:?}, now: {:?}", START_TIME, unstable_now());
if time_elapsed < FRAME_YIELD_MS {
return false;
}