Remove push_future, get use_future kinda working again

This commit is contained in:
Jonathan Kelley 2024-01-15 19:34:04 -08:00
parent 14651a3573
commit a32ae8b112
No known key found for this signature in database
GPG key ID: 1FBB50F7EB0A08BE
8 changed files with 49 additions and 60 deletions

View file

@ -4,9 +4,6 @@
//! This example shows how to encapsulate state in dioxus components with the reducer pattern. //! This example shows how to encapsulate state in dioxus components with the reducer pattern.
//! This pattern is very useful when a single component can handle many types of input that can //! This pattern is very useful when a single component can handle many types of input that can
//! be represented by an enum. //! be represented by an enum.
//!
//! Currently we don't have a reducer pattern hook. If you'd like to add it,
//! feel free to make a PR.
use dioxus::prelude::*; use dioxus::prelude::*;
@ -15,7 +12,7 @@ fn main() {
} }
fn app() -> Element { fn app() -> Element {
let state = use_signal(PlayerState::new); let state = use_signal(|| PlayerState { is_playing: false });
rsx!( rsx!(
div { div {
@ -38,9 +35,6 @@ struct PlayerState {
} }
impl PlayerState { impl PlayerState {
fn new() -> Self {
Self { is_playing: false }
}
fn reduce(&mut self, action: PlayerAction) { fn reduce(&mut self, action: PlayerAction) {
match action { match action {
PlayerAction::Pause => self.is_playing = false, PlayerAction::Pause => self.is_playing = false,

View file

@ -62,14 +62,9 @@ pub fn suspend() -> Option<Element> {
None None
} }
/// Pushes the future onto the poll queue to be polled after the component renders.
pub fn push_future(fut: impl Future<Output = ()> + 'static) -> Option<Task> {
with_current_scope(|cx| cx.push_future(fut))
}
/// Spawns the future but does not return the [`TaskId`] /// Spawns the future but does not return the [`TaskId`]
pub fn spawn(fut: impl Future<Output = ()> + 'static) { pub fn spawn(fut: impl Future<Output = ()> + 'static) -> Task {
with_current_scope(|cx| cx.spawn(fut)); with_current_scope(|cx| cx.spawn(fut)).expect("to be in a dioxus runtime")
} }
/// Spawn a future that Dioxus won't clean up when this component is unmounted /// Spawn a future that Dioxus won't clean up when this component is unmounted

View file

@ -87,9 +87,9 @@ pub mod prelude {
pub use crate::innerlude::{ pub use crate::innerlude::{
consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, generation, consume_context, consume_context_from_scope, current_scope_id, fc_to_builder, generation,
has_context, needs_update, parent_scope, provide_context, provide_root_context, has_context, needs_update, parent_scope, provide_context, provide_root_context,
push_future, remove_future, schedule_update, schedule_update_any, spawn, spawn_forever, remove_future, schedule_update, schedule_update_any, spawn, spawn_forever, suspend,
suspend, try_consume_context, use_error_boundary, use_hook, AnyValue, Attribute, Component, try_consume_context, use_error_boundary, use_hook, AnyValue, Attribute, Component, Element,
Element, ErrorBoundary, Event, EventHandler, Fragment, HasAttributes, IntoAttributeValue, ErrorBoundary, Event, EventHandler, Fragment, HasAttributes, IntoAttributeValue,
IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, ScopeState, Task, Template, IntoDynNode, Properties, Runtime, RuntimeGuard, ScopeId, ScopeState, Task, Template,
TemplateAttribute, TemplateNode, Throw, VNode, VNodeInner, VirtualDom, TemplateAttribute, TemplateNode, Throw, VNode, VNodeInner, VirtualDom,
}; };

View file

@ -1,7 +1,7 @@
use futures_util::task::ArcWake; use futures_util::task::ArcWake;
use super::SchedulerMsg; use super::SchedulerMsg;
use crate::innerlude::{push_future, remove_future, Runtime}; use crate::innerlude::{remove_future, spawn, Runtime};
use crate::ScopeId; use crate::ScopeId;
use std::cell::RefCell; use std::cell::RefCell;
use std::future::Future; use std::future::Future;
@ -28,7 +28,7 @@ impl Task {
/// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which /// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which
/// will only occur when the VirtualDom itself has been dropped. /// will only occur when the VirtualDom itself has been dropped.
pub fn new(task: impl Future<Output = ()> + 'static) -> Self { pub fn new(task: impl Future<Output = ()> + 'static) -> Self {
push_future(task).expect("to be in a dioxus runtime") spawn(task)
} }
/// Drop the task immediately. /// Drop the task immediately.

View file

@ -225,18 +225,13 @@ impl ScopeContext {
.expect("Runtime to exist") .expect("Runtime to exist")
} }
/// Pushes the future onto the poll queue to be polled after the component renders. /// Spawns the future but does not return the [`TaskId`]
pub fn push_future(&self, fut: impl Future<Output = ()> + 'static) -> Task { pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) -> Task {
let id = with_runtime(|rt| rt.spawn(self.id, fut)).expect("Runtime to exist"); let id = with_runtime(|rt| rt.spawn(self.id, fut)).expect("Runtime to exist");
self.spawned_tasks.borrow_mut().insert(id); self.spawned_tasks.borrow_mut().insert(id);
id id
} }
/// Spawns the future but does not return the [`TaskId`]
pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) {
self.push_future(fut);
}
/// Spawn a future that Dioxus won't clean up when this component is unmounted /// Spawn a future that Dioxus won't clean up when this component is unmounted
/// ///
/// This is good for tasks that need to be run after the component has been dropped. /// This is good for tasks that need to be run after the component has been dropped.
@ -369,7 +364,7 @@ impl ScopeId {
/// Pushes the future onto the poll queue to be polled after the component renders. /// Pushes the future onto the poll queue to be polled after the component renders.
pub fn push_future(self, fut: impl Future<Output = ()> + 'static) -> Option<Task> { pub fn push_future(self, fut: impl Future<Output = ()> + 'static) -> Option<Task> {
with_scope(self, |cx| cx.push_future(fut)) with_scope(self, |cx| cx.spawn(fut))
} }
/// Spawns the future but does not return the [`TaskId`] /// Spawns the future but does not return the [`TaskId`]

View file

@ -21,6 +21,7 @@ tracing = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
slab = { workspace = true } slab = { workspace = true }
dioxus-debug-cell = "0.1.1" dioxus-debug-cell = "0.1.1"
futures-util = { workspace = true}
[dev-dependencies] [dev-dependencies]
futures-util = { workspace = true, default-features = false } futures-util = { workspace = true, default-features = false }

View file

@ -1,7 +1,11 @@
#![allow(missing_docs)] #![allow(missing_docs)]
use dioxus_core::{ScopeState, Task}; use dioxus_core::{
prelude::{spawn, use_hook},
ScopeState, Task,
};
use dioxus_signals::{use_effect, use_signal, Signal}; use dioxus_signals::{use_effect, use_signal, Signal};
use std::{any::Any, cell::Cell, future::Future, rc::Rc, sync::Arc}; use futures_util::{future, pin_mut, FutureExt};
use std::{any::Any, cell::Cell, future::Future, pin::Pin, rc::Rc, sync::Arc, task::Poll};
/// A future that resolves to a value. /// A future that resolves to a value.
/// ///
@ -21,22 +25,43 @@ where
T: 'static, T: 'static,
F: Future<Output = T> + 'static, F: Future<Output = T> + 'static,
{ {
let task = use_signal(|| None); let value = use_signal(|| None);
let state = use_signal(|| UseFutureState::Pending);
use_effect(|| { let task = use_signal(|| {
// task.set(); // Create the user's task
let fut = future();
// Spawn a wrapper task that polls the innner future and watch its dependencies
let task = spawn(async move {
// move the future here and pin it so we can ppoll it
let mut fut = fut;
pin_mut!(fut);
let res = future::poll_fn(|cx| {
// Set the effect stack properly
// Poll the inner future
let ready = fut.poll_unpin(cx);
// add any dependencies to the effect stack
ready
})
.await;
// Set the value
value.set(Some(res));
});
Some(task)
}); });
//
UseFuture { UseFuture { task, value, state }
value: todo!(),
task,
state: todo!(),
}
} }
pub struct UseFuture<T: 'static> { pub struct UseFuture<T: 'static> {
value: Signal<T>, value: Signal<Option<T>>,
task: Signal<Option<Task>>, task: Signal<Option<Task>>,
state: Signal<UseFutureState<T>>, state: Signal<UseFutureState<T>>,
} }

View file

@ -42,27 +42,6 @@ pub fn use_effect(callback: impl FnMut() + 'static) {
use_hook(|| Effect::new(callback)); use_hook(|| Effect::new(callback));
} }
/// Create a new effect. The effect will be run immediately and whenever any signal it reads changes.
/// The signal will be owned by the current component and will be dropped when the component is dropped.
pub fn use_effect_with_dependencies<D: Dependency>(
dependencies: D,
mut callback: impl FnMut(D::Out) + 'static,
) where
D::Out: 'static,
{
let dependencies_signal = use_signal(|| dependencies.out());
use_hook(|| {
Effect::new(move || {
let deref = &*dependencies_signal.read();
callback(deref.clone());
});
});
let changed = { dependencies.changed(&*dependencies_signal.read()) };
if changed {
dependencies_signal.set(dependencies.out());
}
}
/// Effects allow you to run code when a signal changes. Effects are run immediately and whenever any signal it reads changes. /// Effects allow you to run code when a signal changes. Effects are run immediately and whenever any signal it reads changes.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct Effect { pub struct Effect {