wip: upgrade hooks

This commit is contained in:
Jonathan Kelley 2021-12-27 15:03:43 -05:00
parent c0e0196a67
commit b3ac2ee3f7
10 changed files with 150 additions and 44 deletions

View file

@ -32,3 +32,24 @@ or use `cargo-edit` to add it via the CLI:
```shell ```shell
$ cargo add dioxus --features desktop $ 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

View file

@ -3,14 +3,14 @@ use quote::{quote, ToTokens, TokenStreamExt};
use syn::{ use syn::{
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
punctuated::Punctuated, 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 struct InlinePropsBody {
pub vis: syn::Visibility, pub vis: syn::Visibility,
pub fn_token: Token![fn], pub fn_token: Token![fn],
pub ident: Ident, pub ident: Ident,
pub cx_token: Ident, pub cx_token: Box<Pat>,
pub generics: Generics, pub generics: Generics,
pub paren_token: token::Paren, pub paren_token: token::Paren,
pub inputs: Punctuated<FnArg, Token![,]>, pub inputs: Punctuated<FnArg, Token![,]>,
@ -31,9 +31,14 @@ impl Parse for InlinePropsBody {
let content; let content;
let paren_token = syn::parenthesized!(content in input); let paren_token = syn::parenthesized!(content in input);
let cx_token: Ident = content.parse()?; let first_arg: FnArg = content.parse()?;
let _: Token![:] = content.parse()?; let cx_token = {
let _: Ident = content.parse()?; match first_arg {
FnArg::Receiver(_) => panic!("first argument must not be a reciver argument"),
FnArg::Typed(f) => f.pat,
}
};
let _: Result<Token![,]> = content.parse(); let _: Result<Token![,]> = content.parse();
let inputs = syn::punctuated::Punctuated::parse_terminated(&content)?; let inputs = syn::punctuated::Punctuated::parse_terminated(&content)?;
@ -84,7 +89,7 @@ impl ToTokens for InlinePropsBody {
let modifiers = if generics.params.is_empty() { let modifiers = if generics.params.is_empty() {
quote! { #[derive(Props, PartialEq)] } quote! { #[derive(Props, PartialEq)] }
} else { } else {
quote! { #[derive(PartialEq)] } quote! { #[derive(Props)] }
}; };
out_tokens.append_all(quote! { out_tokens.append_all(quote! {
@ -93,7 +98,7 @@ impl ToTokens for InlinePropsBody {
#(#fields),* #(#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; let #struct_name { #(#field_names),* } = &cx.props;
#block #block
} }

View file

@ -702,7 +702,7 @@ impl<'a> Future for PollTasks<'a> {
type Output = (); type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
let mut all_pending = true; let mut any_pending = false;
let mut tasks = self.0.tasks.tasks.borrow_mut(); let mut tasks = self.0.tasks.tasks.borrow_mut();
let mut to_remove = vec![]; let mut to_remove = vec![];
@ -712,7 +712,7 @@ impl<'a> Future for PollTasks<'a> {
if task.as_mut().poll(cx).is_ready() { if task.as_mut().poll(cx).is_ready() {
to_remove.push(*id); to_remove.push(*id);
} else { } 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 // Resolve the future if any singular task is ready
match all_pending { match any_pending {
true => Poll::Pending, true => Poll::Pending,
false => Poll::Ready(()), false => Poll::Ready(()),
} }

View file

@ -13,5 +13,8 @@ pub use usecoroutine::*;
mod usefuture; mod usefuture;
pub use usefuture::*; pub use usefuture::*;
mod usesuspense;
pub use usesuspense::*;
// mod usemodel; // mod usemodel;
// pub use usemodel::*; // pub use usemodel::*;

View file

@ -1,4 +1,4 @@
use dioxus_core::ScopeState; use dioxus_core::{ScopeState, TaskId};
use std::future::Future; use std::future::Future;
use std::{cell::Cell, pin::Pin, rc::Rc}; use std::{cell::Cell, pin::Pin, rc::Rc};
/* /*
@ -23,15 +23,17 @@ where
F: Future<Output = ()> + 'static, F: Future<Output = ()> + 'static,
{ {
cx.use_hook( cx.use_hook(
move |_| State { move |_| {
running: Default::default(),
pending_fut: Default::default(),
running_fut: Default::default(),
},
|state| {
let f = create_future(); let f = create_future();
let id = cx.push_future(f); 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))); // state.pending_fut.set(Some(Box::pin(f)));
// if let Some(fut) = state.running_fut.as_mut() { // if let Some(fut) = state.running_fut.as_mut() {
@ -74,13 +76,13 @@ where
struct State { struct State {
running: Rc<Cell<bool>>, running: Rc<Cell<bool>>,
id: TaskId,
// the way this is structure, you can toggle the coroutine without re-rendering the comppnent // 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 // 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 // todo: allocate pending futures in the bump allocator and then have a true promotion
pending_fut: Cell<Option<Pin<Box<dyn Future<Output = ()> + 'static>>>>, // pending_fut: Cell<Option<Pin<Box<dyn Future<Output = ()> + 'static>>>>,
running_fut: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>, // running_fut: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>,
// running_fut: Rc<RefCell<Option<Pin<Box<dyn Future<Output = ()> + 'static>>>>>, // running_fut: Rc<RefCell<Option<Pin<Box<dyn Future<Output = ()> + 'static>>>>>
} }
pub struct CoroutineHandle<'a> { pub struct CoroutineHandle<'a> {
@ -104,11 +106,11 @@ impl<'a> CoroutineHandle<'a> {
return; return;
} }
if let Some(submit) = self.inner.pending_fut.take() { // if let Some(submit) = self.inner.pending_fut.take() {
// submit(); // submit();
// let inner = self.inner; // let inner = self.inner;
// self.cx.push_task(submit()); // self.cx.push_task(submit());
} // }
} }
pub fn is_running(&self) -> bool { pub fn is_running(&self) -> bool {

View file

@ -1,30 +1,52 @@
use dioxus_core::{ScopeState, TaskId}; 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<Output = T>>( pub fn use_future<'a, T: 'static, F: Future<Output = T> + 'static>(
cx: &'a ScopeState, cx: &'a ScopeState,
f: impl FnOnce() -> F, f: impl FnOnce() -> F,
) -> FutureHandle<'a, T> { ) -> (Option<&T>, FutureHandle<'a, T>) {
cx.use_hook( 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 { UseFutureInner {
needs_regen: true, needs_regen: true,
task: None, slot,
value: None,
task: Some(task),
} }
}, },
|_| { |state| {
// if let Some(value) = state.slot.take() {
state.value = Some(value);
state.task = None;
}
(
state.value.as_ref(),
FutureHandle { FutureHandle {
cx, cx,
value: Cell::new(None), value: Cell::new(None),
} },
)
}, },
) )
} }
struct UseFutureInner { struct UseFutureInner<T> {
needs_regen: bool, needs_regen: bool,
value: Option<T>,
slot: Rc<Cell<Option<T>>>,
task: Option<TaskId>, task: Option<TaskId>,
} }

View file

@ -287,11 +287,16 @@ pub struct AsyncUseState<T: 'static> {
wip: Rc<RefCell<Option<T>>>, wip: Rc<RefCell<Option<T>>>,
} }
impl<T: ToOwned> AsyncUseState<T> { impl<T: ToOwned<Owned = T>> AsyncUseState<T> {
pub fn get_mut<'a>(&'a self) -> RefMut<'a, T> { pub fn get_mut<'a>(&'a self) -> RefMut<'a, T> {
// make sure we get processed // 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 // 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 // "get_mut" is locked behind "ToOwned" to make it explicit that cloning occurs to use this
RefMut::map(self.wip.borrow_mut(), |slot| { RefMut::map(self.wip.borrow_mut(), |slot| {
@ -305,9 +310,10 @@ impl<T> AsyncUseState<T> {
(self.re_render)(); (self.re_render)();
*self.wip.borrow_mut() = Some(val); *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<T> { pub fn get_rc(&self) -> &Rc<T> {
&self.inner &self.inner

View file

@ -0,0 +1,44 @@
use std::{cell::Cell, future::Future, rc::Rc};
use dioxus_core::{Element, ScopeState, TaskId};
pub fn use_suspense<R: 'static, F: Future<Output = R> + '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<Cell<Option<R>>> = 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<R> {
task: TaskId,
wip_value: Rc<Cell<Option<R>>>,
value: Option<R>,
}

View file

@ -94,6 +94,8 @@ pub trait GlobalAttributes {
title; title;
translate; translate;
role;
/// dangerous_inner_html is Dioxus's replacement for using innerHTML in the browser DOM. In general, setting /// dangerous_inner_html is Dioxus's replacement for using innerHTML in the browser DOM. In general, setting
/// HTML from code is risky because its easy to inadvertently expose your users to a cross-site scripting (XSS) /// HTML from code is risky because its 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 /// 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", requiredFeatures: "requiredFeatures",
restart: "restart", restart: "restart",
result: "result", result: "result",
role: "role",
rotate: "rotate", rotate: "rotate",
rx: "rx", rx: "rx",
ry: "ry", ry: "ry",

View file

@ -209,7 +209,7 @@ pub mod events {
pub mod prelude { pub mod prelude {
pub use dioxus_core::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_elements::{GlobalAttributes, SvgAttributes};
pub use dioxus_hooks::*; pub use dioxus_hooks::*;
pub use dioxus_html as dioxus_elements; pub use dioxus_html as dioxus_elements;