diff --git a/examples/hello-world/src/App.tsx b/examples/hello-world/src/App.tsx
index a9e1b84..52a923b 100644
--- a/examples/hello-world/src/App.tsx
+++ b/examples/hello-world/src/App.tsx
@@ -1 +1 @@
-export {default} from './perf'
+export {default} from './useContext/index2'
diff --git a/examples/hello-world/src/useContext/index.tsx b/examples/hello-world/src/useContext/index.tsx
new file mode 100644
index 0000000..0e9fd08
--- /dev/null
+++ b/examples/hello-world/src/useContext/index.tsx
@@ -0,0 +1,27 @@
+import {useState, createContext, useContext} from 'react'
+
+const ctxA = createContext('deafult A')
+const ctxB = createContext('default B')
+
+export default function App() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+function Cpn() {
+ const a = useContext(ctxA)
+ const b = useContext(ctxB)
+ return (
+
+ A: {a} B: {b}
+
+ )
+}
diff --git a/examples/hello-world/src/useContext/index2.tsx b/examples/hello-world/src/useContext/index2.tsx
new file mode 100644
index 0000000..6e8f6ac
--- /dev/null
+++ b/examples/hello-world/src/useContext/index2.tsx
@@ -0,0 +1,28 @@
+import {useState, useContext, createContext, useMemo} from 'react'
+
+const ctx = createContext(0)
+
+export default function App() {
+ const [num, update] = useState(0)
+ const memoChild = useMemo(() => {
+ return
+ }, [])
+ console.log('App render ', num)
+ return (
+
+ {
+ update(1)
+ }}>
+ {memoChild}
+
+
+ )
+}
+
+function Child() {
+ console.log('Child render')
+ const val = useContext(ctx)
+
+ return ctx: {val}
+}
diff --git a/packages/react-reconciler/src/begin_work.rs b/packages/react-reconciler/src/begin_work.rs
index d2ecadb..f208e26 100644
--- a/packages/react-reconciler/src/begin_work.rs
+++ b/packages/react-reconciler/src/begin_work.rs
@@ -8,6 +8,7 @@ use web_sys::js_sys::Object;
use crate::child_fiber::{clone_child_fiblers, mount_child_fibers, reconcile_child_fibers};
use crate::fiber::{FiberNode, MemoizedState};
+use crate::fiber_context::push_provider;
use crate::fiber_flags::Flags;
use crate::fiber_hooks::{bailout_hook, render_with_hooks};
use crate::fiber_lanes::{include_some_lanes, Lane};
@@ -108,9 +109,22 @@ pub fn begin_work(
WorkTag::HostRoot => Ok(update_host_root(work_in_progress.clone(), render_lane)),
WorkTag::HostComponent => Ok(update_host_component(work_in_progress.clone())),
WorkTag::HostText => Ok(None),
+ WorkTag::ContextProvider => Ok(update_context_provider(work_in_progress.clone())),
};
}
+fn update_context_provider(
+ work_in_progress: Rc>,
+) -> Option>> {
+ let provider_type = { work_in_progress.borrow()._type.clone() };
+ let context = derive_from_js_value(&provider_type, "_context");
+ let new_props = { work_in_progress.borrow().pending_props.clone() };
+ push_provider(&context, derive_from_js_value(&new_props, "value"));
+ let next_children = derive_from_js_value(&new_props, "children");
+ reconcile_children(work_in_progress.clone(), Some(next_children));
+ work_in_progress.clone().borrow().child.clone()
+}
+
fn update_function_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 5d996e2..4d938ee 100644
--- a/packages/react-reconciler/src/child_fiber.rs
+++ b/packages/react-reconciler/src/child_fiber.rs
@@ -213,7 +213,12 @@ fn update_from_map(
should_track_effects: bool,
) -> Option>> {
let key_to_use;
- if type_of(element, "string") || type_of(element, "null") || type_of(element, "number") {
+ if type_of(element, "string")
+ || type_of(element, "null")
+ || type_of(element, "number")
+ || type_of(element, "undefined")
+ || type_of(element, "null")
+ {
key_to_use = JsValue::from(index);
} else {
let key = derive_from_js_value(element, "key");
diff --git a/packages/react-reconciler/src/commit_work.rs b/packages/react-reconciler/src/commit_work.rs
index 96ac6fe..600aa17 100644
--- a/packages/react-reconciler/src/commit_work.rs
+++ b/packages/react-reconciler/src/commit_work.rs
@@ -12,7 +12,9 @@ use crate::fiber::{FiberNode, FiberRootNode, StateNode};
use crate::fiber_flags::{get_mutation_mask, get_passive_mask, Flags};
use crate::fiber_hooks::Effect;
use crate::work_tags::WorkTag;
-use crate::work_tags::WorkTag::{HostComponent, HostRoot, HostText};
+use crate::work_tags::WorkTag::{
+ ContextProvider, FunctionComponent, HostComponent, HostRoot, HostText,
+};
use crate::HOST_CONFIG;
static mut NEXT_EFFECT: Option>> = None;
@@ -309,20 +311,24 @@ fn commit_deletion(child_to_delete: Rc>, root: Rc {
+ FunctionComponent => {
commit_passive_effect(unmount_fiber.clone(), root.clone(), "unmount");
}
- WorkTag::HostRoot => {}
- WorkTag::HostComponent => {
+ HostRoot => {}
+ HostComponent => {
if cloned.borrow().is_none() {
*cloned.borrow_mut() = Some(unmount_fiber.clone());
}
}
- WorkTag::HostText => {
+ HostText => {
if cloned.borrow().is_none() {
*cloned.borrow_mut() = Some(unmount_fiber.clone());
}
}
+ HostRoot => todo!(),
+ HostComponent => todo!(),
+ HostText => todo!(),
+ ContextProvider => todo!(),
};
});
diff --git a/packages/react-reconciler/src/complete_work.rs b/packages/react-reconciler/src/complete_work.rs
index 415e9bf..caaf693 100644
--- a/packages/react-reconciler/src/complete_work.rs
+++ b/packages/react-reconciler/src/complete_work.rs
@@ -8,6 +8,7 @@ use web_sys::js_sys::{Object, Reflect};
use shared::{derive_from_js_value, log};
use crate::fiber::{FiberNode, StateNode};
+use crate::fiber_context::pop_provider;
use crate::fiber_flags::Flags;
use crate::fiber_lanes::{merge_lanes, Lane};
use crate::work_tags::WorkTag;
@@ -100,7 +101,7 @@ impl CompleteWork {
let mut subtree_flags = Flags::NoFlags;
let mut new_child_lanes = Lane::NoLane;
{
- let mut child = complete_work.clone().borrow().child.clone();
+ let mut child = { complete_work.clone().borrow().child.clone() };
while child.is_some() {
let child_rc = child.clone().unwrap().clone();
@@ -123,7 +124,6 @@ impl CompleteWork {
child = child_rc.borrow().sibling.clone();
}
}
-
complete_work.clone().borrow_mut().subtree_flags |= subtree_flags.clone();
complete_work.clone().borrow_mut().child_lanes |= new_child_lanes.clone();
}
@@ -215,6 +215,13 @@ impl CompleteWork {
self.bubble_properties(work_in_progress.clone());
None
}
+ WorkTag::ContextProvider => {
+ let _type = { work_in_progress.borrow()._type.clone() };
+ let context = derive_from_js_value(&_type, "_context");
+ pop_provider(&context);
+ self.bubble_properties(work_in_progress.clone());
+ None
+ }
}
}
}
diff --git a/packages/react-reconciler/src/fiber.rs b/packages/react-reconciler/src/fiber.rs
index d29bc6e..29be5dd 100644
--- a/packages/react-reconciler/src/fiber.rs
+++ b/packages/react-reconciler/src/fiber.rs
@@ -9,7 +9,7 @@ use scheduler::Task;
use wasm_bindgen::JsValue;
use web_sys::js_sys::Reflect;
-use shared::{derive_from_js_value, log, type_of};
+use shared::{derive_from_js_value, log, type_of, REACT_PROVIDER_TYPE};
use crate::fiber_flags::Flags;
use crate::fiber_hooks::{Effect, Hook};
@@ -109,6 +109,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");
+ }
})
}
}
@@ -147,6 +158,10 @@ impl FiberNode {
let mut fiber_tag = WorkTag::FunctionComponent;
if _type.is_string() {
fiber_tag = WorkTag::HostComponent
+ } else if type_of(&_type, "object")
+ && derive_from_js_value(&_type, "$$typeof") == REACT_PROVIDER_TYPE
+ {
+ fiber_tag = WorkTag::ContextProvider;
} else if !type_of(&_type, "function") {
log!("Unsupported type {:?}", ele);
}
diff --git a/packages/react-reconciler/src/fiber_context.rs b/packages/react-reconciler/src/fiber_context.rs
new file mode 100644
index 0000000..2c9e808
--- /dev/null
+++ b/packages/react-reconciler/src/fiber_context.rs
@@ -0,0 +1,25 @@
+use wasm_bindgen::JsValue;
+use web_sys::js_sys::Reflect;
+
+static mut PREV_CONTEXT_VALUE: JsValue = JsValue::null();
+static mut PREV_CONTEXT_VALUE_STACK: Vec = vec![];
+
+pub fn push_provider(context: &JsValue, new_value: JsValue) {
+ unsafe {
+ PREV_CONTEXT_VALUE_STACK.push(PREV_CONTEXT_VALUE.clone());
+ PREV_CONTEXT_VALUE = Reflect::get(context, &"_currentValue".into()).unwrap();
+ Reflect::set(context, &"_currentValue".into(), &new_value);
+ }
+}
+
+pub fn pop_provider(context: &JsValue) {
+ unsafe {
+ Reflect::set(context, &"_currentValue".into(), &PREV_CONTEXT_VALUE);
+ let top = PREV_CONTEXT_VALUE_STACK.pop();
+ if top.is_none() {
+ PREV_CONTEXT_VALUE = JsValue::null();
+ } else {
+ PREV_CONTEXT_VALUE = top.unwrap();
+ }
+ }
+}
diff --git a/packages/react-reconciler/src/fiber_hooks.rs b/packages/react-reconciler/src/fiber_hooks.rs
index ad46d63..e280e0d 100644
--- a/packages/react-reconciler/src/fiber_hooks.rs
+++ b/packages/react-reconciler/src/fiber_hooks.rs
@@ -5,7 +5,7 @@ use wasm_bindgen::prelude::{wasm_bindgen, Closure};
use wasm_bindgen::{JsCast, JsValue};
use web_sys::js_sys::{Array, Function, Object, Reflect};
-use shared::{is_dev, log};
+use shared::{derive_from_js_value, is_dev, log};
use crate::begin_work::mark_wip_received_update;
use crate::fiber::{FiberNode, MemoizedState};
@@ -146,12 +146,23 @@ fn update_hooks_to_dispatcher(is_update: bool) {
.clone();
use_callback_clusure.forget();
+ // use_context
+ let use_context_clusure =
+ Closure::wrap(Box::new(read_context) as Box JsValue>);
+ let use_context = use_context_clusure
+ .as_ref()
+ .unchecked_ref::()
+ .clone();
+ use_context_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");
+ Reflect::set(&object, &"use_context".into(), &use_context)
+ .expect("TODO: panic set use_context");
updateDispatcher(&object.into());
}
@@ -666,14 +677,15 @@ fn update_memo(create: Function, deps: JsValue) -> Result {
deps
};
- if let MemoizedState::MemoizedJsValue(prev_state) = hook
- .clone()
- .unwrap()
- .borrow()
- .memoized_state
- .as_ref()
- .unwrap()
- {
+ let memoized_state = {
+ hook.clone()
+ .unwrap()
+ .borrow()
+ .memoized_state
+ .clone()
+ .unwrap()
+ };
+ if let MemoizedState::MemoizedJsValue(prev_state) = memoized_state {
if !next_deps.is_null() {
let arr = prev_state.dyn_ref::().unwrap();
let prev_deps = arr.get(1);
@@ -739,3 +751,12 @@ fn update_callback(callback: Function, deps: JsValue) -> JsValue {
}
panic!("update_callback, memoized_state is not JsValue");
}
+
+fn read_context(context: JsValue) -> JsValue {
+ let consumer = unsafe { CURRENTLY_RENDERING_FIBER.clone() };
+ if consumer.is_none() {
+ panic!("Can only call useContext in Function Component");
+ }
+ let value = derive_from_js_value(&context, "_currentValue");
+ value
+}
diff --git a/packages/react-reconciler/src/lib.rs b/packages/react-reconciler/src/lib.rs
index 81e8d52..3b7eb3b 100644
--- a/packages/react-reconciler/src/lib.rs
+++ b/packages/react-reconciler/src/lib.rs
@@ -17,6 +17,7 @@ mod child_fiber;
mod commit_work;
mod complete_work;
pub mod fiber;
+mod fiber_context;
mod fiber_flags;
mod fiber_hooks;
pub mod fiber_lanes;
diff --git a/packages/react-reconciler/src/work_tags.rs b/packages/react-reconciler/src/work_tags.rs
index 3b72990..c16071f 100644
--- a/packages/react-reconciler/src/work_tags.rs
+++ b/packages/react-reconciler/src/work_tags.rs
@@ -4,4 +4,5 @@ pub enum WorkTag {
HostRoot = 3,
HostComponent = 5,
HostText = 6,
+ ContextProvider = 8,
}
diff --git a/packages/react/src/current_dispatcher.rs b/packages/react/src/current_dispatcher.rs
index 9b63a0c..a62e1d6 100644
--- a/packages/react/src/current_dispatcher.rs
+++ b/packages/react/src/current_dispatcher.rs
@@ -2,8 +2,6 @@ use js_sys::{Function, Reflect};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
-use crate::use_callback;
-
#[derive(Debug)]
pub struct Dispatcher {
pub use_state: Function,
@@ -11,6 +9,7 @@ pub struct Dispatcher {
pub use_ref: Function,
pub use_memo: Function,
pub use_callback: Function,
+ pub use_context: Function,
}
unsafe impl Send for Dispatcher {}
@@ -22,6 +21,7 @@ impl Dispatcher {
use_ref: Function,
use_memo: Function,
use_callback: Function,
+ use_context: Function,
) -> Self {
Dispatcher {
use_state,
@@ -29,6 +29,7 @@ impl Dispatcher {
use_ref,
use_memo,
use_callback,
+ use_context,
}
}
}
@@ -53,11 +54,13 @@ pub unsafe fn update_dispatcher(args: &JsValue) {
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");
+ let use_context = derive_function_from_js_value(args, "use_context");
CURRENT_DISPATCHER.current = Some(Box::new(Dispatcher::new(
use_state,
use_effect,
use_ref,
use_memo,
use_callback,
+ use_context,
)))
}
diff --git a/packages/react/src/lib.rs b/packages/react/src/lib.rs
index 7b02db8..1819888 100644
--- a/packages/react/src/lib.rs
+++ b/packages/react/src/lib.rs
@@ -1,7 +1,7 @@
use js_sys::{Array, Object, Reflect, JSON};
use wasm_bindgen::prelude::*;
-use shared::{derive_from_js_value, REACT_ELEMENT_TYPE};
+use shared::{derive_from_js_value, REACT_CONTEXT_TYPE, REACT_ELEMENT_TYPE, REACT_PROVIDER_TYPE};
use crate::current_dispatcher::CURRENT_DISPATCHER;
@@ -140,3 +140,29 @@ pub unsafe fn use_callback(callback: &JsValue, deps: &JsValue) -> Result Result {
+ let use_context = &CURRENT_DISPATCHER.current.as_ref().unwrap().use_context;
+ use_context.call1(&JsValue::null(), context)
+}
+
+#[wasm_bindgen(js_name = createContext)]
+pub unsafe fn create_context(default_value: &JsValue) -> JsValue {
+ let context = Object::new();
+ Reflect::set(
+ &context,
+ &"$$typeof".into(),
+ &JsValue::from_str(REACT_CONTEXT_TYPE),
+ );
+ Reflect::set(&context, &"_currentValue".into(), default_value);
+ let provider = Object::new();
+ Reflect::set(
+ &provider,
+ &"$$typeof".into(),
+ &JsValue::from_str(REACT_PROVIDER_TYPE),
+ );
+ Reflect::set(&provider, &"_context".into(), &context);
+ Reflect::set(&context, &"Provider".into(), &provider);
+ context.into()
+}
diff --git a/packages/shared/src/lib.rs b/packages/shared/src/lib.rs
index 3366641..3740cdf 100644
--- a/packages/shared/src/lib.rs
+++ b/packages/shared/src/lib.rs
@@ -1,8 +1,10 @@
-use web_sys::js_sys::JSON::stringify;
use web_sys::js_sys::Reflect;
+use web_sys::js_sys::JSON::stringify;
use web_sys::wasm_bindgen::JsValue;
pub static REACT_ELEMENT_TYPE: &str = "react.element";
+pub static REACT_CONTEXT_TYPE: &str = "react.context";
+pub static REACT_PROVIDER_TYPE: &str = "react.provider";
#[macro_export]
macro_rules! log {