|
@@ -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) => {}
|
|
|
-
|
|
|
- // Needs to be initialized
|
|
|
- (false, false) => self.add_shared_state(init()),
|
|
|
-
|
|
|
- _ => debug_assert!(false, "Cannot initialize two contexts of the same type"),
|
|
|
- };
|
|
|
+ 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"
|
|
|
+ );
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- 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###"
|