From 975fa566f9809f8fa2bb0bdb07fbfc7f855dcaeb Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Fri, 9 Jul 2021 12:47:41 -0400 Subject: [PATCH] wip: more refactor for async --- Cargo.toml | 2 +- examples/async.rs | 51 ++++++++------------------------ examples/calculator.rs | 23 +++++++------- examples/doggo.rs | 2 +- examples/model.rs | 6 ++-- examples/reducer.rs | 8 +++-- packages/atoms/src/lib.rs | 2 +- packages/core/src/context.rs | 24 +++++---------- packages/core/src/diff.rs | 5 +++- packages/core/src/hooklist.rs | 4 +-- packages/core/src/lib.rs | 1 + packages/core/src/nodes.rs | 13 ++++++-- packages/core/src/tasks.rs | 20 +++++++------ packages/core/src/virtual_dom.rs | 32 ++++++++++++++++---- packages/hooks/src/usestate.rs | 3 +- packages/web/src/new.rs | 15 ++++------ packages/webview/src/dom.rs | 20 ++++--------- src/lib.rs | 1 + 18 files changed, 110 insertions(+), 122 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 89a6a7143..be7e9a2a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ rand = "0.8.4" separator = "0.4.1" serde = { version="1.0.126", features=["derive"] } surf = "2.2.0" - +env_logger = "*" [workspace] members = [ diff --git a/examples/async.rs b/examples/async.rs index 8a1d631e0..98e847cce 100644 --- a/examples/async.rs +++ b/examples/async.rs @@ -7,7 +7,9 @@ use std::pin::Pin; use dioxus::prelude::*; use futures::Future; fn main() { - dioxus::web::launch(App) + env_logger::init(); + log::info!("hello world"); + dioxus::desktop::launch(App, |c| c).expect("faield to launch"); } #[derive(serde::Deserialize)] @@ -17,58 +19,31 @@ struct DogApi { const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random"; +struct Ex(Pin + 'static>>); static App: FC<()> = |cx| { // let mut count = use_state(cx, || 0); let mut fut = cx.use_hook( move || { - Box::pin(async { + Ex(Box::pin(async { // loop { - // repeatadly get new doggos match surf::get(ENDPOINT).recv_json::().await { Ok(_) => (), Err(_) => (), - // Ok(res) => rsx!(in cx, img { src: "{res.message}" }), - // Err(_) => rsx!(in cx, div { "No doggos for you :(" }), } - // wait one seconds } - }) as Pin + 'static>> + }) + as Pin + 'static>>) }, - |h| h, + |h| &mut h.0, |_| {}, ); cx.submit_task(fut); - todo!() - // cx.render(rsx! { - // div { - // h1 { "Hifive counter: {count}" } - // button { onclick: move |_| count += 1, "Up high!" } - // button { onclick: move |_| count -= 1, "Down low!" } - // } - // }) + cx.render(rsx! { + div { + + } + }) }; - -// #[derive(serde::Deserialize)] -// struct DogApi { -// message: String, -// } -// const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random"; - -// pub static App: FC<()> = |cx| { -// let doggo = use_future_effect(&cx, move || async move { -// match surf::get(ENDPOINT).recv_json::().await { -// Ok(res) => rsx!(in cx, img { src: "{res.message}" }), -// Err(_) => rsx!(in cx, div { "No doggos for you :(" }), -// } -// }); - -// cx.render(rsx!( -// div { -// h1 {"Waiting for a doggo..."} -// {doggo} -// } -// )) -// }; diff --git a/examples/calculator.rs b/examples/calculator.rs index 3d9fab0a4..b7bb911c2 100644 --- a/examples/calculator.rs +++ b/examples/calculator.rs @@ -26,19 +26,18 @@ static App: FC<()> = |cx| { let clear_display = display_value.eq("0"); let clear_text = if clear_display { "C" } else { "AC" }; - let input_digit = - move |num: u8| display_value.modify(move |f| f.push_str(num.to_string().as_str())); + let input_digit = move |num: u8| display_value.get_mut().push_str(num.to_string().as_str()); - let input_dot = move || display_value.modify(move |f| f.push_str(".")); + let input_dot = move || display_value.get_mut().push_str("."); let perform_operation = move || { if let Some(op) = operator.as_ref() { let rhs = display_value.parse::().unwrap(); let new_val = match op { - Operator::Add => **cur_val + rhs, - Operator::Sub => **cur_val - rhs, - Operator::Mul => **cur_val * rhs, - Operator::Div => **cur_val / rhs, + Operator::Add => *cur_val + rhs, + Operator::Sub => *cur_val - rhs, + Operator::Mul => *cur_val * rhs, + Operator::Div => *cur_val / rhs, }; cur_val.set(new_val); display_value.set(new_val.to_string()); @@ -50,7 +49,7 @@ static App: FC<()> = |cx| { if display_value.starts_with("-") { display_value.set(display_value.trim_start_matches("-").to_string()) } else { - display_value.set(format!("-{}", **display_value)) + display_value.set(format!("-{}", *display_value)) } }; let toggle_percent = move |_| todo!(); @@ -64,11 +63,11 @@ static App: FC<()> = |cx| { }; let keydownhandler = move |evt: KeyboardEvent| match evt.key_code() { - KeyCode::Backspace => display_value.modify(|f| { - if !f.as_str().eq("0") { - f.pop(); + KeyCode::Backspace => { + if !display_value.as_str().eq("0") { + display_value.get_mut().pop(); } - }), + } KeyCode::_0 => input_digit(0), KeyCode::_1 => input_digit(1), KeyCode::_2 => input_digit(2), diff --git a/examples/doggo.rs b/examples/doggo.rs index 84a1afe9a..46d0302aa 100644 --- a/examples/doggo.rs +++ b/examples/doggo.rs @@ -2,7 +2,7 @@ use dioxus::prelude::*; fn main() {} static Example: FC<()> = |cx| { - let (g, set_g) = use_state_classic(cx, || 0); + let (g, set_g) = use_state(cx, || 0).classic(); let v = (0..10).map(move |f| { rsx!(li { onclick: move |_| set_g(10) diff --git a/examples/model.rs b/examples/model.rs index 6ff982143..bd925a71a 100644 --- a/examples/model.rs +++ b/examples/model.rs @@ -36,9 +36,9 @@ enum Operator { } static App: FC<()> = |cx| { - let (cur_val, set_cur_val) = use_state_classic(cx, || 0.0_f64); - let (operator, set_operator) = use_state_classic(cx, || None as Option); - let (display_value, set_display_value) = use_state_classic(cx, || "0".to_string()); + let (cur_val, set_cur_val) = use_state(cx, || 0.0_f64).classic(); + let (operator, set_operator) = use_state(cx, || None as Option).classic(); + let (display_value, set_display_value) = use_state(cx, || "0".to_string()).classic(); let clear_display = display_value.eq("0"); let clear_text = if clear_display { "C" } else { "AC" }; diff --git a/examples/reducer.rs b/examples/reducer.rs index 65724e856..e72e76a90 100644 --- a/examples/reducer.rs +++ b/examples/reducer.rs @@ -1,5 +1,6 @@ //! Example: Reducer Pattern //! ----------------- +//! //! This example shows how to encapsulate sate in dioxus components with the reducer pattern. //! This pattern is very useful when a single component can handle many types of input that can //! be represented by an enum. @@ -10,7 +11,7 @@ fn main() { } pub static App: FC<()> = |cx| { - let (state, reduce) = use_reducer(cx, PlayerState::new, PlayerState::reduce); + let state = use_state(cx, PlayerState::new); let is_playing = state.is_playing(); @@ -20,11 +21,11 @@ pub static App: FC<()> = |cx| { h3 {"The radio is... {is_playing}!"} button { "Pause" - onclick: move |_| reduce(PlayerAction::Pause) + onclick: move |_| state.get_mut().reduce(PlayerAction::Pause) } button { "Play" - onclick: move |_| reduce(PlayerAction::Play) + onclick: move |_| state.get_mut().reduce(PlayerAction::Play) } } }) @@ -35,6 +36,7 @@ enum PlayerAction { Play, } +#[derive(Clone)] struct PlayerState { is_playing: bool, } diff --git a/packages/atoms/src/lib.rs b/packages/atoms/src/lib.rs index 7cd1869cf..8d2263b38 100644 --- a/packages/atoms/src/lib.rs +++ b/packages/atoms/src/lib.rs @@ -368,7 +368,7 @@ mod root { mod hooks { use super::*; - use dioxus_core::{hooks::use_ref, prelude::Context}; + use dioxus_core::prelude::Context; pub fn use_init_recoil_root

(cx: Context

, cfg: impl Fn(())) { cx.use_create_context(move || RefCell::new(RecoilRoot::new())) diff --git a/packages/core/src/context.rs b/packages/core/src/context.rs index 848212941..c1b5f62a0 100644 --- a/packages/core/src/context.rs +++ b/packages/core/src/context.rs @@ -147,18 +147,18 @@ impl<'src, P> Context<'src, P> { cleanup: impl FnOnce(InternalHookState), ) -> Output { // If the idx is the same as the hook length, then we need to add the current hook - if self.scope.hooks.is_finished() { + if self.scope.hooks.at_end() { let new_state = initializer(); - self.scope.hooks.push(Box::new(new_state)); + self.scope.hooks.push(new_state); } let state = self.scope.hooks.next::().expect( r###" -Unable to retrive the hook that was initialized in this index. -Consult the `rules of hooks` to understand how to use hooks properly. - -You likely used the hook in a conditional. Hooks rely on consistent ordering between renders. -Any function prefixed with "use" should not be called conditionally. + Unable to retrive the hook that was initialized in this index. + Consult the `rules of hooks` to understand how to use hooks properly. + + You likely used the hook in a conditional. Hooks rely on consistent ordering between renders. + Any function prefixed with "use" should not be called conditionally. "###, ); @@ -299,16 +299,8 @@ Any function prefixed with "use" should not be called conditionally. /// /// /// - pub fn submit_task( - &self, - mut task: &'src mut DTask<'src>, - // mut task: &'src mut Pin + 'static>>, - ) -> TaskHandle { + pub fn submit_task(&self, mut task: &'src mut DTask<'src>) -> TaskHandle { self.tasks.push(task); - // let mut g = self.task.borrow_mut(); - // *g = Some(task); - // the pointer to the task is stable - we guarantee stability of all &'src references - // let task_ptr = task as *mut _; TaskHandle { _p: PhantomData {} } } diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index f70ef0b8e..9c268be6a 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -22,7 +22,7 @@ //! More info on how to improve this diffing algorithm: //! - https://hacks.mozilla.org/2019/03/fast-bump-allocated-virtual-doms-with-rust-and-wasm/ -use crate::{arena::SharedArena, innerlude::*}; +use crate::{arena::SharedArena, innerlude::*, tasks::TaskQueue}; use fxhash::{FxHashMap, FxHashSet}; use std::{ @@ -94,6 +94,7 @@ pub trait RealDom<'a> { pub struct DiffMachine<'real, 'bump, Dom: RealDom<'bump>> { pub dom: &'real mut Dom, pub components: &'bump SharedArena, + pub task_queue: &'bump TaskQueue, pub cur_idx: ScopeIdx, pub diffed: FxHashSet, pub event_queue: EventQueue, @@ -106,12 +107,14 @@ impl<'real, 'bump, Dom: RealDom<'bump>> DiffMachine<'real, 'bump, Dom> { components: &'bump SharedArena, cur_idx: ScopeIdx, event_queue: EventQueue, + task_queue: &'bump TaskQueue, ) -> Self { Self { components, dom, cur_idx, event_queue, + task_queue, diffed: FxHashSet::default(), seen_nodes: FxHashSet::default(), } diff --git a/packages/core/src/hooklist.rs b/packages/core/src/hooklist.rs index 00d085f8d..8942d934a 100644 --- a/packages/core/src/hooklist.rs +++ b/packages/core/src/hooklist.rs @@ -59,7 +59,7 @@ impl HookList { } #[inline] - pub(crate) fn is_finished(&self) -> bool { - self.idx.get() == self.vals.len() + pub(crate) fn at_end(&self) -> bool { + self.cur_idx() >= self.len() } } diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index b23b98175..228f585c0 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -61,6 +61,7 @@ pub mod prelude { use crate::nodes; pub use crate::styles::{AsAttr, StyleBuilder}; + pub use crate::util::RealDomNode; pub use nodes::*; pub use crate::nodebuilder::LazyNodes; diff --git a/packages/core/src/nodes.rs b/packages/core/src/nodes.rs index c97096624..c82d31e9a 100644 --- a/packages/core/src/nodes.rs +++ b/packages/core/src/nodes.rs @@ -9,6 +9,7 @@ use crate::{ innerlude::{Context, Properties, RealDom, RealDomNode, Scope, ScopeIdx, FC}, nodebuilder::{text3, NodeFactory}, }; +use appendlist::AppendList; use bumpalo::Bump; use std::{ cell::{Cell, RefCell}, @@ -417,19 +418,25 @@ impl<'a> VComponent<'a> { type Captured<'a> = Rc Fn(&'r Scope) -> VNode<'r> + 'a>; fn create_closure<'a, P: 'a>( - component: FC

, + user_component: FC

, raw_props: *const (), ) -> Rc Fn(&'r Scope) -> VNode<'r>> { let g: Captured = Rc::new(move |scp: &Scope| -> VNode { // cast back into the right lifetime let safe_props: &'_ P = unsafe { &*(raw_props as *const P) }; + let tasks = AppendList::new(); let cx: Context

= Context { props: safe_props, scope: scp, - tasks: todo!(), + tasks: &tasks, }; - let g = component(cx); + let g = user_component(cx); + + // collect the submitted tasks + println!("tasks submittted: {:#?}", tasks.len()); + // log::debug!("tasks submittted: {:#?}", tasks.len()); + let g2 = unsafe { std::mem::transmute(g) }; g2 }); diff --git a/packages/core/src/tasks.rs b/packages/core/src/tasks.rs index 33f7cfd0c..febdaff41 100644 --- a/packages/core/src/tasks.rs +++ b/packages/core/src/tasks.rs @@ -39,15 +39,15 @@ impl TaskQueue { Self { slots, submitter } } - fn push_task(&mut self, task: DTask) -> TaskHandle { + pub fn submit_task(&mut self, task: DTask) -> TaskHandle { let key = self.slots.write().unwrap().insert(task); - TaskHandle {} + TaskHandle { key } } - fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool { self.slots.read().unwrap().is_empty() } - fn len(&self) -> usize { + pub fn len(&self) -> usize { self.slots.read().unwrap().len() } } @@ -99,7 +99,9 @@ impl Stream for TaskQueue { } } -struct TaskHandle {} +pub struct TaskHandle { + key: DefaultKey, +} pub struct DTask { fut: *mut Pin>>, @@ -114,7 +116,7 @@ impl DTask { dead: Cell::new(false), } } - fn debug_new(fut: &mut Pin>>) -> Self { + pub fn debug_new(fut: &mut Pin>>) -> Self { let originator = ScopeIdx::default(); Self { fut, @@ -154,9 +156,9 @@ mod tests { }) as RawTask); let mut queue = TaskQueue::new(); - queue.push_task(DTask::debug_new(f1)); - queue.push_task(DTask::debug_new(f2)); - queue.push_task(DTask::debug_new(f3)); + queue.submit_task(DTask::debug_new(f1)); + queue.submit_task(DTask::debug_new(f2)); + queue.submit_task(DTask::debug_new(f3)); while !queue.is_empty() { let next = queue.next().await; diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 0baea5655..c8d63bdb1 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -21,6 +21,7 @@ use crate::tasks::TaskQueue; use crate::{arena::SharedArena, innerlude::*}; +use appendlist::AppendList; use slotmap::DefaultKey; use slotmap::SlotMap; use std::{any::TypeId, fmt::Debug, rc::Rc}; @@ -130,16 +131,29 @@ impl VirtualDom { // Normally, a component would be passed as a child in the RSX macro which automatically produces OpaqueComponents // Here, we need to make it manually, using an RC to force the Weak reference to stick around for the main scope. - let _root_caller: Rc = Rc::new(move |scope| { + let _root_caller: Rc = Rc::new(move |scope: &Scope| { // let _root_caller: Rc> = Rc::new(move |scope| { // the lifetime of this closure is just as long as the lifetime on the scope reference // this closure moves root props (which is static) into this closure let props = unsafe { &*(&root_props as *const _) }; - root(Context { + let tasks = AppendList::new(); + let t2 = &tasks; + + let cx = Context { props, scope, - tasks: todo!(), - }) + tasks: t2, + }; + let nodes = root(cx); + + log::debug!("There were {:?} tasks submitted", tasks.len()); + // cast a weird lifetime to shake the appendlist thing + // TODO: move all of this into the same logic that governs other components + // we want to wrap everything in a dioxus root component + unsafe { std::mem::transmute(nodes) } + // std::mem::drop(tasks); + // + // nodes }); // Create a weak reference to the OpaqueComponent for the root scope to use as its render function @@ -185,6 +199,7 @@ impl VirtualDom { &self.components, self.base_scope, self.event_queue.clone(), + &self.tasks, ); // Schedule an update and then immediately call it on the root component @@ -251,8 +266,13 @@ impl VirtualDom { self.components.try_get_mut(id)?.call_listener(trigger)?; - let mut diff_machine = - DiffMachine::new(realdom, &self.components, id, self.event_queue.clone()); + let mut diff_machine = DiffMachine::new( + realdom, + &self.components, + id, + self.event_queue.clone(), + &self.tasks, + ); self.progress_completely(&mut diff_machine)?; diff --git a/packages/hooks/src/usestate.rs b/packages/hooks/src/usestate.rs index 5600636ca..c95ca80b6 100644 --- a/packages/hooks/src/usestate.rs +++ b/packages/hooks/src/usestate.rs @@ -69,7 +69,6 @@ struct UseStateInner { update_scheuled: Cell, callback: Rc, wip: RefCell>, - updater: } pub struct UseState<'a, T: 'static> { @@ -109,7 +108,7 @@ impl<'a, T: 'static> UseState<'a, T> { } pub fn classic(self) -> (&'a T, &'a Rc) { - (&self.inner.current_val) + todo!() } } impl<'a, T: 'static + ToOwned> UseState<'a, T> { diff --git a/packages/web/src/new.rs b/packages/web/src/new.rs index 6e2eadfa8..eeb4b547d 100644 --- a/packages/web/src/new.rs +++ b/packages/web/src/new.rs @@ -2,8 +2,7 @@ use std::{collections::HashMap, rc::Rc, sync::Arc}; use dioxus_core::{ events::{EventTrigger, VirtualEvent}, - prelude::ScopeIdx, - virtual_dom::RealDomNode, + prelude::{RealDomNode, ScopeIdx}, }; use fxhash::FxHashMap; use slotmap::{DefaultKey, Key, KeyData}; @@ -72,7 +71,7 @@ impl WebsysDom { } impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom { - fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) { + fn push_root(&mut self, root: RealDomNode) { log::debug!("Called [push_root] {:?}", root); let key: DefaultKey = KeyData::from_ffi(root.0).into(); let domnode = self.nodes.get(key).expect("Failed to pop know root"); @@ -150,7 +149,7 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom { fn create_placeholder(&mut self) -> RealDomNode { self.create_element("pre", None) } - fn create_text_node(&mut self, text: &str) -> dioxus_core::virtual_dom::RealDomNode { + fn create_text_node(&mut self, text: &str) -> RealDomNode { // let nid = self.node_counter.next(); let textnode = self .document @@ -166,11 +165,7 @@ impl<'a> dioxus_core::diff::RealDom<'a> for WebsysDom { RealDomNode::new(nid) } - fn create_element( - &mut self, - tag: &str, - ns: Option<&'static str>, - ) -> dioxus_core::virtual_dom::RealDomNode { + fn create_element(&mut self, tag: &str, ns: Option<&'static str>) -> RealDomNode { let tag = wasm_bindgen::intern(tag); let el = match ns { Some(ns) => self @@ -587,7 +582,7 @@ fn decode_trigger(event: &web_sys::Event) -> anyhow::Result { Ok(EventTrigger::new( virtual_event_from_websys_event(event), triggered_scope, - real_id, + Some(real_id), dioxus_core::events::EventPriority::High, )) } diff --git a/packages/webview/src/dom.rs b/packages/webview/src/dom.rs index f8bec436d..6c79dcd9e 100644 --- a/packages/webview/src/dom.rs +++ b/packages/webview/src/dom.rs @@ -2,11 +2,7 @@ use dioxus_core as dioxus; use dioxus_core::prelude::*; -use dioxus_core::{ - diff::RealDom, - serialize::DomEdit, - virtual_dom::{RealDomNode, VirtualDom}, -}; +use dioxus_core::{diff::RealDom, serialize::DomEdit, virtual_dom::VirtualDom}; use DomEdit::*; pub struct WebviewRegistry {} @@ -37,7 +33,7 @@ impl WebviewDom<'_> { } } impl<'bump> RealDom<'bump> for WebviewDom<'bump> { - fn push_root(&mut self, root: dioxus_core::virtual_dom::RealDomNode) { + fn push_root(&mut self, root: RealDomNode) { self.edits.push(PushRoot { root: root.0 }); } @@ -57,18 +53,14 @@ impl<'bump> RealDom<'bump> for WebviewDom<'bump> { self.edits.push(RemoveAllChildren); } - fn create_text_node(&mut self, text: &'bump str) -> dioxus_core::virtual_dom::RealDomNode { + fn create_text_node(&mut self, text: &'bump str) -> RealDomNode { self.node_counter += 1; let id = RealDomNode::new(self.node_counter); self.edits.push(CreateTextNode { text, id: id.0 }); id } - fn create_element( - &mut self, - tag: &'bump str, - ns: Option<&'bump str>, - ) -> dioxus_core::virtual_dom::RealDomNode { + fn create_element(&mut self, tag: &'bump str, ns: Option<&'bump str>) -> RealDomNode { self.node_counter += 1; let id = RealDomNode::new(self.node_counter); match ns { @@ -78,7 +70,7 @@ impl<'bump> RealDom<'bump> for WebviewDom<'bump> { id } - fn create_placeholder(&mut self) -> dioxus_core::virtual_dom::RealDomNode { + fn create_placeholder(&mut self) -> RealDomNode { self.node_counter += 1; let id = RealDomNode::new(self.node_counter); self.edits.push(CreatePlaceholder { id: id.0 }); @@ -90,7 +82,7 @@ impl<'bump> RealDom<'bump> for WebviewDom<'bump> { event: &'static str, scope: dioxus_core::prelude::ScopeIdx, element_id: usize, - realnode: dioxus_core::virtual_dom::RealDomNode, + realnode: RealDomNode, ) { self.edits.push(NewEventListener { scope, diff --git a/src/lib.rs b/src/lib.rs index 7bb844769..09086221e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -169,6 +169,7 @@ pub mod prelude { //! A glob import that includes helper types like FC, rsx!, html!, and required traits pub use dioxus_core::prelude::*; pub use dioxus_core_macro::fc; + pub use dioxus_hooks::*; pub use dioxus_html as dioxus_elements; } // pub mod builder {