feat: shared state mechanisms
This commit is contained in:
1 parent
da4423c141
commit
4a4c7afca7
12 files changed
+279
-120
No files matched your search
@@ -102,6 +102,10 @@ impl<'src> Context<'src> {
|
||||
self.scope.memoized_updater.clone()
|
||||
}
|
||||
|
||||
pub fn needs_update(&self) {
|
||||
(self.scope.memoized_updater)()
|
||||
}
|
||||
|
||||
/// Schedule an update for any component given its ScopeId.
|
||||
///
|
||||
/// A component's ScopeId can be obtained from `use_hook` or the [`Context::scope_id`] method.
|
||||
@@ -178,65 +182,33 @@ impl<'src> Context<'src> {
|
||||
/// struct SharedState(&'static str);
|
||||
///
|
||||
/// static App: FC<()> = |cx, props|{
|
||||
/// cx.use_provide_state(|| SharedState("world"));
|
||||
/// cx.provide_state(SharedState("world"));
|
||||
/// rsx!(cx, Child {})
|
||||
/// }
|
||||
///
|
||||
/// static Child: FC<()> = |cx, props|{
|
||||
/// let state = cx.use_consume_state::<SharedState>();
|
||||
/// let state = cx.consume_state::<SharedState>();
|
||||
/// rsx!(cx, div { "hello {state.0}" })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn use_provide_state<T, F>(self, init: F) -> &'src Rc<T>
|
||||
pub fn provide_state<T>(self, value: T) -> Option<Rc<T>>
|
||||
where
|
||||
T: 'static,
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
let is_initialized = self.use_hook(
|
||||
|_| false,
|
||||
|s| {
|
||||
let i = *s;
|
||||
*s = true;
|
||||
i
|
||||
},
|
||||
|_| {},
|
||||
);
|
||||
|
||||
if !is_initialized {
|
||||
let existing = self
|
||||
.scope
|
||||
.shared_contexts
|
||||
.borrow_mut()
|
||||
.insert(TypeId::of::<T>(), Rc::new(init()));
|
||||
|
||||
if existing.is_some() {
|
||||
log::warn!(
|
||||
"A shared state was replaced with itself. \
|
||||
This is does not result in a panic, but is probably not what you are trying to do"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.use_consume_state().unwrap()
|
||||
self.scope
|
||||
.shared_contexts
|
||||
.borrow_mut()
|
||||
.insert(TypeId::of::<T>(), Rc::new(value))
|
||||
.map(|f| f.downcast::<T>().ok())
|
||||
.flatten()
|
||||
}
|
||||
|
||||
/// Uses a context, storing the cached value around
|
||||
///
|
||||
/// If a context is not found on the first search, then this call will be "dud", always returning "None" even if a
|
||||
/// context was added later. This allows using another hook as a fallback
|
||||
///
|
||||
pub fn use_consume_state<T: 'static>(self) -> Option<&'src Rc<T>> {
|
||||
struct UseContextHook<C>(Option<Rc<C>>);
|
||||
self.use_hook(
|
||||
move |_| {
|
||||
let getter = &self.scope.shared.get_shared_context;
|
||||
let ty = TypeId::of::<T>();
|
||||
let idx = self.scope.our_arena_idx;
|
||||
UseContextHook(getter(idx, ty).map(|f| f.downcast().unwrap()))
|
||||
},
|
||||
move |hook| hook.0.as_ref(),
|
||||
|_| {},
|
||||
)
|
||||
/// Try to retrive a SharedState with type T from the any parent Scope.
|
||||
pub fn consume_state<T: 'static>(self) -> Option<Rc<T>> {
|
||||
let getter = &self.scope.shared.get_shared_context;
|
||||
let ty = TypeId::of::<T>();
|
||||
let idx = self.scope.our_arena_idx;
|
||||
getter(idx, ty).map(|f| f.downcast().unwrap())
|
||||
}
|
||||
|
||||
/// Create a new subtree with this scope as the root of the subtree.
|
||||
@@ -250,15 +222,11 @@ impl<'src> Context<'src> {
|
||||
///
|
||||
/// ```rust
|
||||
/// static App: FC<()> = |cx, props| {
|
||||
/// let id = cx.get_current_subtree();
|
||||
/// let id = cx.use_create_subtree();
|
||||
/// subtree {
|
||||
///
|
||||
/// }
|
||||
/// todo!();
|
||||
/// rsx!(cx, div { "Subtree {id}"})
|
||||
/// };
|
||||
/// ```
|
||||
pub fn use_create_subtree(self) -> Option<u32> {
|
||||
pub fn create_subtree(self) -> Option<u32> {
|
||||
self.scope.new_subtree()
|
||||
}
|
||||
|
||||
|
||||
+16
-33
@@ -326,7 +326,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||
}
|
||||
|
||||
for attr in *attributes {
|
||||
self.mutations.set_attribute(attr);
|
||||
self.mutations.set_attribute(attr, real_id.as_u64());
|
||||
}
|
||||
|
||||
if !children.is_empty() {
|
||||
@@ -428,9 +428,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||
fn diff_text_nodes(&mut self, old: &'bump VText<'bump>, new: &'bump VText<'bump>) {
|
||||
if let Some(root) = old.dom_id.get() {
|
||||
if old.text != new.text {
|
||||
self.mutations.push_root(root);
|
||||
self.mutations.set_text(new.text);
|
||||
self.mutations.pop();
|
||||
self.mutations.set_text(new.text, root.as_u64());
|
||||
}
|
||||
|
||||
new.dom_id.set(Some(root));
|
||||
@@ -443,7 +441,7 @@ impl<'bump> DiffMachine<'bump> {
|
||||
new: &'bump VElement<'bump>,
|
||||
new_node: &'bump VNode<'bump>,
|
||||
) {
|
||||
let root = old.dom_id.get();
|
||||
let root = old.dom_id.get().unwrap();
|
||||
|
||||
// If the element type is completely different, the element needs to be re-rendered completely
|
||||
// This is an optimization React makes due to how users structure their code
|
||||
@@ -462,24 +460,13 @@ impl<'bump> DiffMachine<'bump> {
|
||||
return;
|
||||
}
|
||||
|
||||
new.dom_id.set(root);
|
||||
new.dom_id.set(Some(root));
|
||||
|
||||
// todo: attributes currently rely on the element on top of the stack, but in theory, we only need the id of the
|
||||
// element to modify its attributes.
|
||||
// it would result in fewer instructions if we just set the id directly.
|
||||
// it would also clean up this code some, but that's not very important anyways
|
||||
|
||||
// Don't push the root if we don't have to
|
||||
let mut has_comitted = false;
|
||||
let mut please_commit = |edits: &mut Vec<DomEdit>| {
|
||||
if !has_comitted {
|
||||
has_comitted = true;
|
||||
edits.push(PushRoot {
|
||||
root: root.unwrap().as_u64(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Diff Attributes
|
||||
//
|
||||
// It's extraordinarily rare to have the number/order of attributes change
|
||||
@@ -489,20 +476,17 @@ impl<'bump> DiffMachine<'bump> {
|
||||
if old.attributes.len() == new.attributes.len() {
|
||||
for (old_attr, new_attr) in old.attributes.iter().zip(new.attributes.iter()) {
|
||||
if old_attr.value != new_attr.value {
|
||||
please_commit(&mut self.mutations.edits);
|
||||
self.mutations.set_attribute(new_attr);
|
||||
self.mutations.set_attribute(new_attr, root.as_u64());
|
||||
} else if new_attr.is_volatile {
|
||||
please_commit(&mut self.mutations.edits);
|
||||
self.mutations.set_attribute(new_attr);
|
||||
self.mutations.set_attribute(new_attr, root.as_u64());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
please_commit(&mut self.mutations.edits);
|
||||
for attribute in old.attributes {
|
||||
self.mutations.remove_attribute(attribute);
|
||||
self.mutations.remove_attribute(attribute, root.as_u64());
|
||||
}
|
||||
for attribute in new.attributes {
|
||||
self.mutations.set_attribute(attribute)
|
||||
self.mutations.set_attribute(attribute, root.as_u64())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -520,20 +504,20 @@ impl<'bump> DiffMachine<'bump> {
|
||||
if old.listeners.len() == new.listeners.len() {
|
||||
for (old_l, new_l) in old.listeners.iter().zip(new.listeners.iter()) {
|
||||
if old_l.event != new_l.event {
|
||||
please_commit(&mut self.mutations.edits);
|
||||
self.mutations.remove_event_listener(old_l.event);
|
||||
self.mutations
|
||||
.remove_event_listener(old_l.event, root.as_u64());
|
||||
self.mutations.new_event_listener(new_l, cur_scope_id);
|
||||
}
|
||||
new_l.mounted_node.set(old_l.mounted_node.get());
|
||||
self.attach_listener_to_scope(new_l, scope);
|
||||
}
|
||||
} else {
|
||||
please_commit(&mut self.mutations.edits);
|
||||
for listener in old.listeners {
|
||||
self.mutations.remove_event_listener(listener.event);
|
||||
self.mutations
|
||||
.remove_event_listener(listener.event, root.as_u64());
|
||||
}
|
||||
for listener in new.listeners {
|
||||
listener.mounted_node.set(root);
|
||||
listener.mounted_node.set(Some(root));
|
||||
self.mutations.new_event_listener(listener, cur_scope_id);
|
||||
self.attach_listener_to_scope(listener, scope);
|
||||
}
|
||||
@@ -541,13 +525,12 @@ impl<'bump> DiffMachine<'bump> {
|
||||
}
|
||||
|
||||
if old.children.len() == 0 && new.children.len() != 0 {
|
||||
please_commit(&mut self.mutations.edits);
|
||||
self.mutations.edits.push(PushRoot {
|
||||
root: root.as_u64(),
|
||||
});
|
||||
self.stack.create_children(new.children, MountType::Append);
|
||||
} else {
|
||||
self.diff_children(old.children, new.children);
|
||||
if has_comitted {
|
||||
self.mutations.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,16 +90,16 @@ impl<'a> Mutations<'a> {
|
||||
root: element_id,
|
||||
});
|
||||
}
|
||||
pub(crate) fn remove_event_listener(&mut self, event: &'static str) {
|
||||
self.edits.push(RemoveEventListener { event });
|
||||
pub(crate) fn remove_event_listener(&mut self, event: &'static str, root: u64) {
|
||||
self.edits.push(RemoveEventListener { event, root });
|
||||
}
|
||||
|
||||
// modify
|
||||
pub(crate) fn set_text(&mut self, text: &'a str) {
|
||||
self.edits.push(SetText { text });
|
||||
pub(crate) fn set_text(&mut self, text: &'a str, root: u64) {
|
||||
self.edits.push(SetText { text, root });
|
||||
}
|
||||
|
||||
pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute) {
|
||||
pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute, root: u64) {
|
||||
let Attribute {
|
||||
name,
|
||||
value,
|
||||
@@ -111,12 +111,13 @@ impl<'a> Mutations<'a> {
|
||||
field: name,
|
||||
value,
|
||||
ns: *namespace,
|
||||
root,
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn remove_attribute(&mut self, attribute: &Attribute) {
|
||||
pub(crate) fn remove_attribute(&mut self, attribute: &Attribute, root: u64) {
|
||||
let name = attribute.name;
|
||||
self.edits.push(RemoveAttribute { name });
|
||||
self.edits.push(RemoveAttribute { name, root });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,17 +207,21 @@ pub enum DomEdit<'bump> {
|
||||
root: u64,
|
||||
},
|
||||
RemoveEventListener {
|
||||
root: u64,
|
||||
event: &'static str,
|
||||
},
|
||||
SetText {
|
||||
root: u64,
|
||||
text: &'bump str,
|
||||
},
|
||||
SetAttribute {
|
||||
root: u64,
|
||||
field: &'static str,
|
||||
value: &'bump str,
|
||||
ns: Option<&'bump str>,
|
||||
},
|
||||
RemoveAttribute {
|
||||
root: u64,
|
||||
name: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user