From cca7c5fc3a9e341996e2d5a23b981b658e54ea62 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Wed, 7 Jul 2021 15:07:46 -0400 Subject: [PATCH] wip: remove the scoped trait --- packages/atoms/src/lib.rs | 28 ++--- packages/core/src/hooks.rs | 22 ++-- packages/core/src/lib.rs | 1 - packages/core/src/virtual_dom.rs | 193 ++++++++++--------------------- 4 files changed, 85 insertions(+), 159 deletions(-) diff --git a/packages/atoms/src/lib.rs b/packages/atoms/src/lib.rs index b7a6b0f63..7cd1869cf 100644 --- a/packages/atoms/src/lib.rs +++ b/packages/atoms/src/lib.rs @@ -32,7 +32,7 @@ mod traits { // Atoms, selectors, and their family variants are readable pub trait Readable: Sized + Copy { fn use_read<'a, P: 'static>(self, cx: Context<'a, P>) -> &'a T { - hooks::use_read(&cx, self) + hooks::use_read(cx, self) } // This returns a future of the value @@ -95,9 +95,9 @@ mod atoms { const EXAMPLE_ATOM: Atom = |_| 10; // ensure that atoms are both read and write - let _ = use_read(&cx, &EXAMPLE_ATOM); - let _ = use_read_write(&cx, &EXAMPLE_ATOM); - let _ = use_write(&cx, &EXAMPLE_ATOM); + let _ = use_read(cx, &EXAMPLE_ATOM); + let _ = use_read_write(cx, &EXAMPLE_ATOM); + let _ = use_write(cx, &EXAMPLE_ATOM); } } } @@ -161,7 +161,7 @@ mod atomfamily { fn test(cx: Context<()>) { let title = Titles.select(&10).use_read(cx); - let t2 = use_read(&cx, &Titles.select(&10)); + let t2 = use_read(cx, &Titles.select(&10)); } } } @@ -368,7 +368,7 @@ mod root { mod hooks { use super::*; - use dioxus_core::{hooks::use_ref, prelude::Context, virtual_dom::Scoped}; + use dioxus_core::{hooks::use_ref, prelude::Context}; pub fn use_init_recoil_root

(cx: Context

, cfg: impl Fn(())) { cx.use_create_context(move || RefCell::new(RecoilRoot::new())) @@ -381,12 +381,12 @@ mod hooks { /// /// You can use this method to create controllers that perform much more complex actions than set/get /// However, be aware that "getting" values through this hook will not subscribe the component to any updates. - pub fn use_recoil_api<'a>(cx: &impl Scoped<'a>) -> &'a Rc { + pub fn use_recoil_api<'a, P>(cx: Context<'a, P>) -> &'a Rc { cx.use_context::() } - pub fn use_write<'a, T: AtomValue>( - cx: &impl Scoped<'a>, + pub fn use_write<'a, P, T: AtomValue>( + cx: Context<'a, P>, // todo: this shouldn't need to be static writable: impl Writable, ) -> &'a Rc { @@ -412,8 +412,8 @@ mod hooks { /// Read the atom and get the Rc directly to the Atom's slot /// This is useful if you need the memoized Atom value. However, Rc is not as easy to /// work with as - pub fn use_read_raw<'a, T: AtomValue>( - cx: &impl Scoped<'a>, + pub fn use_read_raw<'a, P, T: AtomValue>( + cx: Context<'a, P>, readable: impl Readable, ) -> &'a Rc { struct ReadHook { @@ -449,7 +449,7 @@ mod hooks { } /// - pub fn use_read<'a, T: AtomValue>(cx: &impl Scoped<'a>, readable: impl Readable) -> &'a T { + pub fn use_read<'a, P, T: AtomValue>(cx: Context<'a, P>, readable: impl Readable) -> &'a T { use_read_raw(cx, readable).as_ref() } @@ -468,8 +468,8 @@ mod hooks { /// // equivalent to: /// let (title, set_title) = (use_read(cx, &Title), use_write(cx, &Title)); /// ``` - pub fn use_read_write<'a, T: AtomValue + 'static>( - cx: &impl Scoped<'a>, + pub fn use_read_write<'a, P, T: AtomValue + 'static>( + cx: Context<'a, P>, writable: impl Writable, ) -> (&'a T, &'a Rc) { (use_read(cx, writable), use_write(cx, writable)) diff --git a/packages/core/src/hooks.rs b/packages/core/src/hooks.rs index b5fcecf6a..2b04abc96 100644 --- a/packages/core/src/hooks.rs +++ b/packages/core/src/hooks.rs @@ -36,8 +36,8 @@ use std::{ /// } /// } /// ``` -pub fn use_state_classic<'a, 'c, T: 'static, F: FnOnce() -> T>( - cx: impl Scoped<'a>, +pub fn use_state_classic<'a, 'c, T: 'static, F: FnOnce() -> T, P>( + cx: Context<'a, P>, initial_state_fn: F, ) -> (&'a T, &'a Rc) { struct UseState { @@ -158,8 +158,8 @@ impl<'a, T: 'static + Display> std::fmt::Display for UseState { /// } /// } /// ``` -pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T>( - cx: impl Scoped<'a>, +pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T, P>( + cx: Context<'a, P>, initial_state_fn: F, ) -> &'a UseState { cx.use_hook( @@ -230,8 +230,8 @@ pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T>( /// To change it, use modify. /// Modifications to this value do not cause updates to the component /// Attach to inner context reference, so context can be consumed -pub fn use_ref<'a, T: 'static>( - cx: impl Scoped<'a>, +pub fn use_ref<'a, T: 'static, P>( + cx: Context<'a, P>, initial_state_fn: impl FnOnce() -> T + 'static, ) -> &'a RefCell { cx.use_hook(|| RefCell::new(initial_state_fn()), |state| &*state, |_| {}) @@ -249,8 +249,8 @@ struct UseReducer { /// /// This is behaves almost exactly the same way as React's "use_state". /// -pub fn use_reducer<'a, 'c, State: 'static, Action: 'static>( - cx: impl Scoped<'a>, +pub fn use_reducer<'a, 'c, State: 'static, Action: 'static, P>( + cx: Context<'a, P>, initial_state_fn: impl FnOnce() -> State, _reducer: impl Fn(&mut State, Action), ) -> (&'a State, &'a Box) { @@ -291,8 +291,8 @@ pub fn use_reducer<'a, 'c, State: 'static, Action: 'static>( /// Use model makes it easy to use "models" as state for components. To modify the model, call "modify" and a clone of the /// current model will be made, with a RefMut lock on it. Dioxus will never run your components multithreaded, so you can /// be relatively sure that this won't fail in practice -pub fn use_model<'a, T: ToOwned + 'static>( - cx: impl Scoped<'a>, +pub fn use_model<'a, T: ToOwned + 'static, P>( + cx: Context<'a, P>, f: impl FnOnce() -> T, ) -> &'a UseModel { cx.use_hook( @@ -381,7 +381,7 @@ mod tests { // }; } -pub fn use_is_initialized

(cx: Context

) -> bool { +pub fn use_is_initialized<'a, P>(cx: Context<'a, P>) -> bool { let val = use_ref(cx, || false); match *val.borrow() { true => true, diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index 933117881..b74c641ef 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -57,7 +57,6 @@ pub mod prelude { use crate::nodes; pub use crate::styles::{AsAttr, StyleBuilder}; pub use crate::virtual_dom::Context; - pub use crate::virtual_dom::Scoped; pub use nodes::*; pub use crate::nodebuilder::LazyNodes; diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 4faeaafc0..5e3fe3888 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -586,13 +586,58 @@ impl Scope { pub(crate) fn root<'a>(&'a self) -> &'a VNode<'a> { &self.frames.cur_frame().head_node } +} +/// Components in Dioxus use the "Context" object to interact with their lifecycle. +/// This lets components schedule updates, integrate hooks, and expose their context via the context api. +/// +/// Properties passed down from the parent component are also directly accessible via the exposed "props" field. +/// +/// ```ignore +/// #[derive(Properties)] +/// struct Props { +/// name: String +/// +/// } +/// +/// fn example(cx: Context) -> VNode { +/// html! { +///

"Hello, {cx.name}"
+/// } +/// } +/// ``` +// todo: force lifetime of source into T as a valid lifetime too +// it's definitely possible, just needs some more messing around +pub struct Context<'src, T> { + pub props: &'src T, + pub scope: &'src Scope, +} + +impl<'src, T> Copy for Context<'src, T> {} +impl<'src, T> Clone for Context<'src, T> { + fn clone(&self) -> Self { + Self { + props: self.props, + scope: self.scope, + } + } +} + +impl<'a, T> Deref for Context<'a, T> { + type Target = &'a T; + + fn deref(&self) -> &Self::Target { + &self.props + } +} + +impl<'src, P> Context<'src, P> { /// Access the children elements passed into the component - pub fn children(&self) -> &[VNode] { + pub fn children(&self) -> &'src [VNode<'src>] { // We're re-casting the nodes back out // They don't really have a static lifetime unsafe { - let scope = self; + let scope = self.scope; let nodes = scope.child_nodes; nodes } @@ -600,7 +645,7 @@ impl Scope { /// Create a subscription that schedules a future render for the reference component pub fn schedule_update(&self) -> Rc { - self.event_channel.clone() + self.scope.event_channel.clone() } pub fn schedule_effect(&self) -> Rc { @@ -626,11 +671,11 @@ impl Scope { /// cx.render(lazy_tree) /// } ///``` - pub fn render<'src, F: FnOnce(NodeFactory<'src>) -> VNode<'src>>( - &'src self, + pub fn render) -> VNode<'src>>( + self, lazy_nodes: LazyNodes<'src, F>, ) -> VNode<'src> { - let scope_ref = self; + let scope_ref = self.scope; let listener_id = &scope_ref.listener_idx; lazy_nodes.into_vnode(NodeFactory { scope_ref, @@ -654,8 +699,8 @@ impl Scope { /// ) /// } /// ``` - pub fn use_hook<'src, InternalHookState: 'static, Output: 'src>( - &'src self, + pub fn use_hook( + self, // The closure that builds the hook state initializer: impl FnOnce() -> InternalHookState, @@ -667,7 +712,7 @@ impl Scope { // TODO: add this to the "clean up" group for when the component is dropped _cleanup: impl FnOnce(InternalHookState), ) -> Output { - let scope = self; + let scope = self.scope; let idx = scope.hookidx.get(); @@ -715,8 +760,8 @@ Any function prefixed with "use" should not be called conditionally. /// /// /// - fn use_create_context(&self, init: impl Fn() -> T) { - let scope = self; + pub fn use_create_context(&self, init: impl Fn() -> T) { + let mut scope = self.scope; let mut cxs = scope.shared_contexts.borrow_mut(); let ty = TypeId::of::(); @@ -745,12 +790,12 @@ Any function prefixed with "use" should not be called conditionally. } /// There are hooks going on here! - pub fn use_context<'src, T: 'static>(&'src self) -> &'src Rc { + pub fn use_context(self) -> &'src Rc { self.try_use_context().unwrap() } /// Uses a context, storing the cached value around - pub fn try_use_context<'src, T: 'static>(&'src self) -> Result<&'src Rc> { + pub fn try_use_context(self) -> Result<&'src Rc> { struct UseContextHook { par: Option>, we: Option>, @@ -762,7 +807,7 @@ Any function prefixed with "use" should not be called conditionally. we: None as Option>, }, move |hook| { - let mut scope = Some(self); + let mut scope = Some(self.scope); if let Some(we) = &hook.we { if let Some(re) = we.upgrade() { @@ -807,11 +852,7 @@ Any function prefixed with "use" should not be called conditionally. ) } - pub fn suspend< - 'src, - Output: 'src, - Fut: FnOnce(SuspendedContext, Output) -> VNode<'src> + 'src, - >( + pub fn suspend VNode<'src> + 'src>( &'src self, fut: &'src mut Pin + 'static>>, callback: Fut, @@ -832,120 +873,6 @@ Any function prefixed with "use" should not be called conditionally. } } -/// Components in Dioxus use the "Context" object to interact with their lifecycle. -/// This lets components schedule updates, integrate hooks, and expose their context via the context api. -/// -/// Properties passed down from the parent component are also directly accessible via the exposed "props" field. -/// -/// ```ignore -/// #[derive(Properties)] -/// struct Props { -/// name: String -/// -/// } -/// -/// fn example(cx: Context) -> VNode { -/// html! { -///
"Hello, {cx.name}"
-/// } -/// } -/// ``` -// todo: force lifetime of source into T as a valid lifetime too -// it's definitely possible, just needs some more messing around -pub struct Context<'src, T> { - pub props: &'src T, - pub scope: &'src Scope, -} - -impl<'src, T> Copy for Context<'src, T> {} -impl<'src, T> Clone for Context<'src, T> { - fn clone(&self) -> Self { - Self { - props: self.props, - scope: self.scope, - } - } -} - -impl<'a, T> Deref for Context<'a, T> { - type Target = &'a T; - - fn deref(&self) -> &Self::Target { - &self.props - } -} -/// The `Scoped` trait makes it slightly less cumbersome for hooks to use the underlying `Scope` without having to make -/// their hooks generic over the [`Props`] type that [`Context`] is -/// -/// ## Example: -/// -/// ``` -/// fn use_raw(cx: impl Scoped) -> &mut T { -/// -/// } -/// ``` -/// -pub trait Scoped<'src>: Sized + Clone + Copy { - /// - /// - /// - /// - /// - /// - /// - fn get_scope(self) -> &'src Scope; - - #[inline] - fn children(self) -> &'src [VNode<'src>] { - self.get_scope().children() - } - - #[inline] - fn schedule_update(self) -> Rc { - self.get_scope().schedule_update() - } - - #[inline] - fn use_hook( - self, - initializer: impl FnOnce() -> InternalHookState, - runner: impl FnOnce(&'src mut InternalHookState) -> Output, - _cleanup: impl FnOnce(InternalHookState), - ) -> Output { - self.get_scope().use_hook(initializer, runner, _cleanup) - } - - /// 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. - /// - /// ## Example - /// - /// ```ignore - /// fn Component(cx: Context<()>) -> VNode { - /// // Lazy assemble the VNode tree - /// let lazy_tree = html! {
"Hello World"
}; - /// - /// // Actually build the tree and allocate it - /// cx.render(lazy_tree) - /// } - ///``` - #[inline] - fn render) -> VNode<'src>>( - self, - lazy_nodes: LazyNodes<'src, F>, - ) -> VNode<'src> { - self.get_scope().render(lazy_nodes) - } -} - -impl<'src, T> Scoped<'src> for Context<'src, T> { - #[inline] - fn get_scope(self) -> &'src Scope { - self.scope - } -} - #[derive(Clone)] pub struct SuspendedContext {}