From b3ac2ee3f76549cd1c7b6f9eee7e3382b07d873c Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Mon, 27 Dec 2021 15:03:43 -0500 Subject: [PATCH] wip: upgrade hooks --- docs/guide/src/tutorial/new_app.md | 21 ++++++++++++ packages/core-macro/src/inlineprops.rs | 19 +++++++---- packages/core/src/virtual_dom.rs | 6 ++-- packages/hooks/src/lib.rs | 3 ++ packages/hooks/src/usecoroutine.rs | 36 +++++++++++---------- packages/hooks/src/usefuture.rs | 42 ++++++++++++++++++------ packages/hooks/src/usestate.rs | 18 +++++++---- packages/hooks/src/usesuspense.rs | 44 ++++++++++++++++++++++++++ packages/html/src/global_attributes.rs | 3 ++ src/lib.rs | 2 +- 10 files changed, 150 insertions(+), 44 deletions(-) create mode 100644 packages/hooks/src/usesuspense.rs diff --git a/docs/guide/src/tutorial/new_app.md b/docs/guide/src/tutorial/new_app.md index 689b6eaf1..1fb928cfc 100644 --- a/docs/guide/src/tutorial/new_app.md +++ b/docs/guide/src/tutorial/new_app.md @@ -32,3 +32,24 @@ or use `cargo-edit` to add it via the CLI: ```shell $ cargo add dioxus --features desktop ``` + +## Setting up a hello world + +Let's edit the project's `main.rs` and add the skeleton of + +```rust +use dioxus::prelude::*; + +fn main() { + dioxus::desktop::launch(app); +} + +fn app(cx: Scope) -> Element { + cx.render(rsx!( + div { "hello world!" } + )) +} +``` + + +## Making sure things run diff --git a/packages/core-macro/src/inlineprops.rs b/packages/core-macro/src/inlineprops.rs index ece53622f..9b6efbf36 100644 --- a/packages/core-macro/src/inlineprops.rs +++ b/packages/core-macro/src/inlineprops.rs @@ -3,14 +3,14 @@ use quote::{quote, ToTokens, TokenStreamExt}; use syn::{ parse::{Parse, ParseStream}, punctuated::Punctuated, - token, Block, FnArg, Generics, Ident, Result, ReturnType, Token, Visibility, + token, Block, FnArg, Generics, Ident, Pat, Result, ReturnType, Token, Visibility, }; pub struct InlinePropsBody { pub vis: syn::Visibility, pub fn_token: Token![fn], pub ident: Ident, - pub cx_token: Ident, + pub cx_token: Box, pub generics: Generics, pub paren_token: token::Paren, pub inputs: Punctuated, @@ -31,9 +31,14 @@ impl Parse for InlinePropsBody { let content; let paren_token = syn::parenthesized!(content in input); - let cx_token: Ident = content.parse()?; - let _: Token![:] = content.parse()?; - let _: Ident = content.parse()?; + let first_arg: FnArg = content.parse()?; + let cx_token = { + match first_arg { + FnArg::Receiver(_) => panic!("first argument must not be a reciver argument"), + FnArg::Typed(f) => f.pat, + } + }; + let _: Result = content.parse(); let inputs = syn::punctuated::Punctuated::parse_terminated(&content)?; @@ -84,7 +89,7 @@ impl ToTokens for InlinePropsBody { let modifiers = if generics.params.is_empty() { quote! { #[derive(Props, PartialEq)] } } else { - quote! { #[derive(PartialEq)] } + quote! { #[derive(Props)] } }; out_tokens.append_all(quote! { @@ -93,7 +98,7 @@ impl ToTokens for InlinePropsBody { #(#fields),* } - #vis fn #ident #generics (#cx_token: Scope<#struct_name>) #output { + #vis fn #ident #generics (#cx_token: Scope<'a, #struct_name #generics>) #output { let #struct_name { #(#field_names),* } = &cx.props; #block } diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index f429fead5..32e7c2534 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -702,7 +702,7 @@ impl<'a> Future for PollTasks<'a> { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - let mut all_pending = true; + let mut any_pending = false; let mut tasks = self.0.tasks.tasks.borrow_mut(); let mut to_remove = vec![]; @@ -712,7 +712,7 @@ impl<'a> Future for PollTasks<'a> { if task.as_mut().poll(cx).is_ready() { to_remove.push(*id); } else { - all_pending = false; + any_pending = true; } } @@ -721,7 +721,7 @@ impl<'a> Future for PollTasks<'a> { } // Resolve the future if any singular task is ready - match all_pending { + match any_pending { true => Poll::Pending, false => Poll::Ready(()), } diff --git a/packages/hooks/src/lib.rs b/packages/hooks/src/lib.rs index 4e5c5ce25..1a38ed172 100644 --- a/packages/hooks/src/lib.rs +++ b/packages/hooks/src/lib.rs @@ -13,5 +13,8 @@ pub use usecoroutine::*; mod usefuture; pub use usefuture::*; +mod usesuspense; +pub use usesuspense::*; + // mod usemodel; // pub use usemodel::*; diff --git a/packages/hooks/src/usecoroutine.rs b/packages/hooks/src/usecoroutine.rs index 65cbbd711..b01326345 100644 --- a/packages/hooks/src/usecoroutine.rs +++ b/packages/hooks/src/usecoroutine.rs @@ -1,4 +1,4 @@ -use dioxus_core::ScopeState; +use dioxus_core::{ScopeState, TaskId}; use std::future::Future; use std::{cell::Cell, pin::Pin, rc::Rc}; /* @@ -23,15 +23,17 @@ where F: Future + 'static, { cx.use_hook( - move |_| State { - running: Default::default(), - pending_fut: Default::default(), - running_fut: Default::default(), - }, - |state| { + move |_| { let f = create_future(); let id = cx.push_future(f); - + State { + running: Default::default(), + id + // pending_fut: Default::default(), + // running_fut: Default::default(), + } + }, + |state| { // state.pending_fut.set(Some(Box::pin(f))); // if let Some(fut) = state.running_fut.as_mut() { @@ -74,13 +76,13 @@ where struct State { running: Rc>, - + id: TaskId, // the way this is structure, you can toggle the coroutine without re-rendering the comppnent // this means every render *generates* the future, which is a bit of a waste // todo: allocate pending futures in the bump allocator and then have a true promotion - pending_fut: Cell + 'static>>>>, - running_fut: Option + 'static>>>, - // running_fut: Rc + 'static>>>>>, + // pending_fut: Cell + 'static>>>>, + // running_fut: Option + 'static>>>, + // running_fut: Rc + 'static>>>>> } pub struct CoroutineHandle<'a> { @@ -104,11 +106,11 @@ impl<'a> CoroutineHandle<'a> { return; } - if let Some(submit) = self.inner.pending_fut.take() { - // submit(); - // let inner = self.inner; - // self.cx.push_task(submit()); - } + // if let Some(submit) = self.inner.pending_fut.take() { + // submit(); + // let inner = self.inner; + // self.cx.push_task(submit()); + // } } pub fn is_running(&self) -> bool { diff --git a/packages/hooks/src/usefuture.rs b/packages/hooks/src/usefuture.rs index c2b6ad0da..8ca014e6b 100644 --- a/packages/hooks/src/usefuture.rs +++ b/packages/hooks/src/usefuture.rs @@ -1,30 +1,52 @@ use dioxus_core::{ScopeState, TaskId}; -use std::{cell::Cell, future::Future}; +use std::{cell::Cell, future::Future, rc::Rc}; -pub fn use_future<'a, T: 'static, F: Future>( +pub fn use_future<'a, T: 'static, F: Future + 'static>( cx: &'a ScopeState, f: impl FnOnce() -> F, -) -> FutureHandle<'a, T> { +) -> (Option<&T>, FutureHandle<'a, T>) { cx.use_hook( |_| { // + let fut = f(); + let slot = Rc::new(Cell::new(None)); + let updater = cx.schedule_update(); + + let _slot = slot.clone(); + let new_fut = async move { + let res = fut.await; + _slot.set(Some(res)); + updater(); + }; + let task = cx.push_future(new_fut); + UseFutureInner { needs_regen: true, - task: None, + slot, + value: None, + task: Some(task), } }, - |_| { - // - FutureHandle { - cx, - value: Cell::new(None), + |state| { + if let Some(value) = state.slot.take() { + state.value = Some(value); + state.task = None; } + ( + state.value.as_ref(), + FutureHandle { + cx, + value: Cell::new(None), + }, + ) }, ) } -struct UseFutureInner { +struct UseFutureInner { needs_regen: bool, + value: Option, + slot: Rc>>, task: Option, } diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 3286c95a6..03bcf9c4c 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -287,11 +287,16 @@ pub struct AsyncUseState { wip: Rc>>, } -impl AsyncUseState { +impl> AsyncUseState { pub fn get_mut<'a>(&'a self) -> RefMut<'a, T> { // make sure we get processed - // self.needs_update(); - + { + let mut wip = self.wip.borrow_mut(); + if wip.is_none() { + *wip = Some(self.inner.as_ref().to_owned()); + } + (self.re_render)(); + } // Bring out the new value, cloning if it we need to // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this RefMut::map(self.wip.borrow_mut(), |slot| { @@ -305,9 +310,10 @@ impl AsyncUseState { (self.re_render)(); *self.wip.borrow_mut() = Some(val); } - pub fn get(&self) -> &T { - self.inner.as_ref() - } + + // pub fn get(&self) -> Ref<'_, T> { + // self.wip.borrow + // } pub fn get_rc(&self) -> &Rc { &self.inner diff --git a/packages/hooks/src/usesuspense.rs b/packages/hooks/src/usesuspense.rs new file mode 100644 index 000000000..e40246b64 --- /dev/null +++ b/packages/hooks/src/usesuspense.rs @@ -0,0 +1,44 @@ +use std::{cell::Cell, future::Future, rc::Rc}; + +use dioxus_core::{Element, ScopeState, TaskId}; + +pub fn use_suspense + 'static>( + cx: &ScopeState, + create_future: impl FnOnce() -> F, + render: impl FnOnce(&R) -> Element, +) -> Element { + cx.use_hook( + |_| { + let fut = create_future(); + + let wip_value: Rc>> = Default::default(); + + let wip = wip_value.clone(); + let new_fut = async move { + let val = fut.await; + wip.set(Some(val)); + }; + + let task = cx.push_future(new_fut); + SuspenseInner { + task, + value: None, + wip_value, + } + }, + |sus| { + if let Some(value) = sus.value.as_ref() { + render(&value) + } else { + // generate a placeholder node if the future isnt ready + None + } + }, + ) +} + +struct SuspenseInner { + task: TaskId, + wip_value: Rc>>, + value: Option, +} diff --git a/packages/html/src/global_attributes.rs b/packages/html/src/global_attributes.rs index 97c777124..a0bd4a679 100644 --- a/packages/html/src/global_attributes.rs +++ b/packages/html/src/global_attributes.rs @@ -94,6 +94,8 @@ pub trait GlobalAttributes { title; translate; + role; + /// dangerous_inner_html is Dioxus's replacement for using innerHTML in the browser DOM. In general, setting /// HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) /// attack. So, you can set HTML directly from Dioxus, but you have to type out dangerous_inner_html to remind @@ -838,6 +840,7 @@ pub trait SvgAttributes { requiredFeatures: "requiredFeatures", restart: "restart", result: "result", + role: "role", rotate: "rotate", rx: "rx", ry: "ry", diff --git a/src/lib.rs b/src/lib.rs index 98d99449a..a5e89bddb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -209,7 +209,7 @@ pub mod events { pub mod prelude { pub use dioxus_core::prelude::*; - pub use dioxus_core_macro::{format_args_f, rsx, Props, Routable}; + pub use dioxus_core_macro::{format_args_f, inline_props, rsx, Props, Routable}; pub use dioxus_elements::{GlobalAttributes, SvgAttributes}; pub use dioxus_hooks::*; pub use dioxus_html as dioxus_elements;