mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 06:30:20 +00:00
wip: before scheduler simplify
This commit is contained in:
parent
9971ff215d
commit
c16b92ee7e
6 changed files with 77 additions and 53 deletions
2
packages/core/.vscode/settings.json
vendored
2
packages/core/.vscode/settings.json
vendored
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"rust-analyzer.inlayHints.enable": true
|
||||
"rust-analyzer.inlayHints.enable": false
|
||||
}
|
||||
|
|
|
@ -98,15 +98,26 @@ impl<'src, P> Context<'src, P> {
|
|||
/// Create a subscription that schedules a future render for the reference component
|
||||
///
|
||||
/// ## Notice: you should prefer using prepare_update and get_scope_id
|
||||
///
|
||||
pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
|
||||
self.scope.memoized_updater.clone()
|
||||
}
|
||||
|
||||
pub fn prepare_update(&self) -> Rc<dyn Fn(ScopeId)> {
|
||||
/// 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.
|
||||
///
|
||||
/// This method should be used when you want to schedule an update for a component
|
||||
pub fn schedule_update_any(&self) -> Rc<dyn Fn(ScopeId)> {
|
||||
self.scope.shared.schedule_any_immediate.clone()
|
||||
}
|
||||
|
||||
/// Get the [`ScopeId`] of a mounted component.
|
||||
///
|
||||
/// `ScopeId` is not unique for the lifetime of the VirtualDom - a ScopeId will be reused if a component is unmounted.
|
||||
pub fn scope_id(&self) -> ScopeId {
|
||||
self.scope.our_arena_idx
|
||||
}
|
||||
|
||||
/// Take a lazy VNode structure and actually build it with the context of the VDom's efficient VNode allocator.
|
||||
///
|
||||
/// This function consumes the context and absorb the lifetime, so these VNodes *must* be returned.
|
||||
|
@ -151,24 +162,6 @@ impl<'src, P> Context<'src, P> {
|
|||
(self.scope.shared.submit_task)(task)
|
||||
}
|
||||
|
||||
/// Add a state globally accessible to child components via tree walking
|
||||
pub fn add_shared_state<T: 'static>(self, val: T) {
|
||||
self.scope
|
||||
.shared_contexts
|
||||
.borrow_mut()
|
||||
.insert(TypeId::of::<T>(), Rc::new(val))
|
||||
.map(|_| {
|
||||
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");
|
||||
});
|
||||
}
|
||||
|
||||
pub fn try_consume_shared_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().expect("TypeID already validated"))
|
||||
}
|
||||
|
||||
/// This hook enables the ability to expose state to children further down the VirtualDOM Tree.
|
||||
///
|
||||
/// This is a hook, so it should not be called conditionally!
|
||||
|
@ -185,45 +178,44 @@ impl<'src, P> Context<'src, P> {
|
|||
/// struct SharedState(&'static str);
|
||||
///
|
||||
/// static App: FC<()> = |cx| {
|
||||
/// cx.provide_state(|| SharedState("world"));
|
||||
/// cx.use_provide_state(|| SharedState("world"));
|
||||
/// rsx!(cx, Child {})
|
||||
/// }
|
||||
///
|
||||
/// static Child: FC<()> = |cx| {
|
||||
/// let state = cx.consume_state::<SharedState>();
|
||||
/// let state = cx.use_consume_state::<SharedState>();
|
||||
/// rsx!(cx, div { "hello {state.0}" })
|
||||
/// }
|
||||
/// ```
|
||||
pub fn provide_state<T, F>(self, init: F) -> &'src Rc<T>
|
||||
pub fn use_provide_state<T, F>(self, init: F) -> &'src Rc<T>
|
||||
where
|
||||
T: 'static,
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
//
|
||||
let ty = TypeId::of::<T>();
|
||||
let contains_key = self.scope.shared_contexts.borrow().contains_key(&ty);
|
||||
|
||||
let is_initialized = self.use_hook(
|
||||
|_| false,
|
||||
|s| {
|
||||
let i = s.clone();
|
||||
let i = *s;
|
||||
*s = true;
|
||||
i
|
||||
},
|
||||
|_| {},
|
||||
);
|
||||
|
||||
match (is_initialized, contains_key) {
|
||||
// Do nothing, already initialized and already exists
|
||||
(true, true) => {}
|
||||
if !is_initialized {
|
||||
self.scope
|
||||
.shared_contexts
|
||||
.borrow_mut()
|
||||
.insert(TypeId::of::<T>(), Rc::new(init()))
|
||||
.map(|_| {
|
||||
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"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Needs to be initialized
|
||||
(false, false) => self.add_shared_state(init()),
|
||||
|
||||
_ => debug_assert!(false, "Cannot initialize two contexts of the same type"),
|
||||
};
|
||||
|
||||
self.consume_state().unwrap()
|
||||
self.use_consume_state().unwrap()
|
||||
}
|
||||
|
||||
/// Uses a context, storing the cached value around
|
||||
|
@ -231,10 +223,15 @@ impl<'src, P> Context<'src, P> {
|
|||
/// 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 consume_state<T: 'static>(self) -> Option<&'src Rc<T>> {
|
||||
pub fn use_consume_state<T: 'static>(self) -> Option<&'src Rc<T>> {
|
||||
struct UseContextHook<C>(Option<Rc<C>>);
|
||||
self.use_hook(
|
||||
move |_| UseContextHook(self.try_consume_shared_state::<T>()),
|
||||
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(),
|
||||
|_| {},
|
||||
)
|
||||
|
@ -262,19 +259,21 @@ impl<'src, P> Context<'src, P> {
|
|||
self,
|
||||
initializer: Init,
|
||||
runner: Run,
|
||||
_cleanup: Cleanup,
|
||||
cleanup: Cleanup,
|
||||
) -> Output
|
||||
where
|
||||
State: 'static,
|
||||
Output: 'src,
|
||||
Init: FnOnce(usize) -> State,
|
||||
Run: FnOnce(&'src mut State) -> Output,
|
||||
Cleanup: FnOnce(State),
|
||||
Cleanup: FnOnce(&mut State) + 'static,
|
||||
{
|
||||
// If the idx is the same as the hook length, then we need to add the current hook
|
||||
if self.scope.hooks.at_end() {
|
||||
let new_state = initializer(self.scope.hooks.len());
|
||||
self.scope.hooks.push(new_state);
|
||||
self.scope.hooks.push_hook(
|
||||
initializer(self.scope.hooks.len()),
|
||||
Box::new(|raw| cleanup(raw.downcast_mut::<State>().unwrap())),
|
||||
);
|
||||
}
|
||||
|
||||
const ERR_MSG: &str = r###"
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::{
|
|||
/// Todo: this could use its very own bump arena, but that might be a tad overkill
|
||||
#[derive(Default)]
|
||||
pub(crate) struct HookList {
|
||||
vals: RefCell<Vec<UnsafeCell<Box<dyn Any>>>>,
|
||||
vals: RefCell<Vec<(UnsafeCell<Box<dyn Any>>, Box<dyn FnOnce(&mut dyn Any)>)>>,
|
||||
idx: Cell<usize>,
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ impl HookList {
|
|||
pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
|
||||
self.vals.borrow().get(self.idx.get()).and_then(|inn| {
|
||||
self.idx.set(self.idx.get() + 1);
|
||||
let raw_box = unsafe { &mut *inn.get() };
|
||||
let raw_box = unsafe { &mut *inn.0.get() };
|
||||
raw_box.downcast_mut::<T>()
|
||||
})
|
||||
}
|
||||
|
@ -35,8 +35,10 @@ impl HookList {
|
|||
self.idx.set(0);
|
||||
}
|
||||
|
||||
pub(crate) fn push<T: 'static>(&self, new: T) {
|
||||
self.vals.borrow_mut().push(UnsafeCell::new(Box::new(new)))
|
||||
pub(crate) fn push_hook<T: 'static>(&self, new: T, cleanup: Box<dyn FnOnce(&mut dyn Any)>) {
|
||||
self.vals
|
||||
.borrow_mut()
|
||||
.push((UnsafeCell::new(Box::new(new)), cleanup))
|
||||
}
|
||||
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
|
@ -50,4 +52,11 @@ impl HookList {
|
|||
pub(crate) fn at_end(&self) -> bool {
|
||||
self.cur_idx() >= self.len()
|
||||
}
|
||||
|
||||
pub(crate) fn cleanup_hooks(&mut self) {
|
||||
self.vals
|
||||
.borrow_mut()
|
||||
.drain(..)
|
||||
.for_each(|(mut state, cleanup)| cleanup(state.get_mut()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ use std::{
|
|||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
future::Future,
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
|
@ -47,7 +48,7 @@ where
|
|||
|
||||
let slot = task_dump.clone();
|
||||
|
||||
let updater = cx.prepare_update();
|
||||
let updater = cx.schedule_update_any();
|
||||
let originator = cx.scope.our_arena_idx;
|
||||
|
||||
let handle = cx.submit_task(Box::pin(task_fut.then(move |output| async move {
|
||||
|
@ -188,7 +189,14 @@ impl<'src> SuspendedContext<'src> {
|
|||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct NodeRef<'src, T: 'static>(&'src RefCell<T>);
|
||||
pub struct NodeRef<'src, T: 'static>(&'src RefCell<Option<T>>);
|
||||
|
||||
impl<'a, T> Deref for NodeRef<'a, T> {
|
||||
type Target = RefCell<Option<T>>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_node_ref<T, P>(cx: Context<P>) -> NodeRef<T> {
|
||||
cx.use_hook(
|
||||
|
|
|
@ -657,9 +657,17 @@ impl TaskHandle {
|
|||
}
|
||||
}
|
||||
|
||||
/// A component's unique identifier.
|
||||
///
|
||||
/// `ScopeId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
|
||||
/// unmounted, then the `ScopeId` will be reused for a new component.
|
||||
#[derive(serde::Serialize, serde::Deserialize, Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ScopeId(pub usize);
|
||||
|
||||
/// An Element's unique identifier.
|
||||
///
|
||||
/// `ElementId` is a `usize` that is unique across the entire VirtualDOM - but not unique across time. If a component is
|
||||
/// unmounted, then the `ElementId` will be reused for a new component.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ElementId(pub usize);
|
||||
impl Display for ElementId {
|
||||
|
|
|
@ -16,12 +16,12 @@ fn shared_state_test() {
|
|||
struct MySharedState(&'static str);
|
||||
|
||||
static App: FC<()> = |cx| {
|
||||
cx.provide_state(|| MySharedState("world!"));
|
||||
cx.use_provide_state(|| MySharedState("world!"));
|
||||
rsx!(cx, Child {})
|
||||
};
|
||||
|
||||
static Child: FC<()> = |cx| {
|
||||
let shared = cx.consume_state::<MySharedState>()?;
|
||||
let shared = cx.use_consume_state::<MySharedState>()?;
|
||||
rsx!(cx, "Hello, {shared.0}")
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue