implement use_async_memo with reactive context
This commit is contained in:
7 files changed
+68
-58
No files matched your search
+13
-1
@@ -1,3 +1,5 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use dioxus::prelude::*;
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -23,7 +25,17 @@ fn app() -> Element {
|
|||||||
fn Child(sig: Signal<i32>) -> Element {
|
fn Child(sig: Signal<i32>) -> Element {
|
||||||
let doubled = use_memo(move || sig() * 2);
|
let doubled = use_memo(move || sig() * 2);
|
||||||
|
|
||||||
|
let tripled = use_async_memo(move || async move {
|
||||||
|
tokio::time::sleep(Duration::from_millis(200)).await;
|
||||||
|
sig() * 3
|
||||||
|
});
|
||||||
|
|
||||||
|
let trippled = use_memo(move || match tripled.value() {
|
||||||
|
Some(v) => v.cloned(),
|
||||||
|
None => 1338,
|
||||||
|
});
|
||||||
|
|
||||||
rsx! {
|
rsx! {
|
||||||
"The count is: {sig}, doubled: {doubled}"
|
"The count is: {sig}, doubled: {doubled}, tripled: {trippled}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::use_hook_did_run;
|
use crate::use_hook_did_run;
|
||||||
use dioxus_core::prelude::*;
|
use dioxus_core::prelude::*;
|
||||||
use dioxus_signals::{CopyValue, Effect, ReactiveContext, Writable};
|
use dioxus_signals::{CopyValue, Effect, ReactiveContext, Writable};
|
||||||
use futures_util::select;
|
// use futures_util::select;
|
||||||
|
|
||||||
/// Create a new effect. The effect will be run immediately and whenever any signal it reads changes.
|
/// 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.
|
/// The signal will be owned by the current component and will be dropped when the component is dropped.
|
||||||
|
|||||||
@@ -57,13 +57,8 @@ pub fn use_maybe_sync_memo<R: PartialEq, S: Storage<SignalData<R>>>(
|
|||||||
spawn(async move {
|
spawn(async move {
|
||||||
loop {
|
loop {
|
||||||
rc.changed().await;
|
rc.changed().await;
|
||||||
|
|
||||||
println!("change triggered in memo");
|
|
||||||
|
|
||||||
let new = rc.run_in(|| f());
|
let new = rc.run_in(|| f());
|
||||||
|
|
||||||
if new != *state.peek() {
|
if new != *state.peek() {
|
||||||
println!("change sett in memo");
|
|
||||||
*state.write() = new;
|
*state.write() = new;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#![allow(missing_docs)]
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use crate::use_signal;
|
use crate::{use_callback, use_signal};
|
||||||
use dioxus_core::{
|
use dioxus_core::{
|
||||||
prelude::{spawn, suspend},
|
prelude::{spawn, suspend, use_hook},
|
||||||
Task,
|
Task,
|
||||||
};
|
};
|
||||||
use dioxus_signals::*;
|
use dioxus_signals::*;
|
||||||
@@ -19,31 +19,44 @@ where
|
|||||||
F: Future<Output = T> + 'static,
|
F: Future<Output = T> + 'static,
|
||||||
{
|
{
|
||||||
let mut value = use_signal(|| None);
|
let mut value = use_signal(|| None);
|
||||||
let state = use_signal(|| UseResourceState::Pending);
|
let mut state = use_signal(|| UseResourceState::Pending);
|
||||||
|
let rc = use_hook(|| ReactiveContext::new(None));
|
||||||
|
|
||||||
let task = use_signal(|| {
|
let mut cb = use_callback(move || {
|
||||||
// Create the user's task
|
// Create the user's task
|
||||||
let fut = future();
|
let fut = rc.run_in(|| future());
|
||||||
|
|
||||||
// Spawn a wrapper task that polls the innner future and watch its dependencies
|
// Spawn a wrapper task that polls the innner future and watch its dependencies
|
||||||
let task = spawn(async move {
|
spawn(async move {
|
||||||
// // move the future here and pin it so we can poll it
|
// move the future here and pin it so we can poll it
|
||||||
// let fut = fut;
|
let fut = fut;
|
||||||
// pin_mut!(fut);
|
pin_mut!(fut);
|
||||||
|
|
||||||
// let res = future::poll_fn(|cx| {
|
// Run each poll in the context of the reactive scope
|
||||||
// // Set the effect stack properly
|
// This ensures the scope is properly subscribed to the future's dependencies
|
||||||
// // add any dependencies to the effect stack that we need to watch when restarting the future
|
let res = future::poll_fn(|cx| rc.run_in(|| fut.poll_unpin(cx))).await;
|
||||||
// // Poll the inner future
|
|
||||||
// fut.poll_unpin(cx)
|
|
||||||
// })
|
|
||||||
// .await;
|
|
||||||
|
|
||||||
// // Set the value
|
// Set the value and state
|
||||||
// value.set(Some(Signal::new(res)));
|
state.set(UseResourceState::Complete);
|
||||||
});
|
value.set(Some(Signal::new(res)));
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
Some(task)
|
let mut task = use_hook(|| Signal::new(cb.call()));
|
||||||
|
|
||||||
|
use_hook(|| {
|
||||||
|
spawn(async move {
|
||||||
|
loop {
|
||||||
|
// Wait for the dependencies to change
|
||||||
|
rc.changed().await;
|
||||||
|
|
||||||
|
// Stop the old task
|
||||||
|
task.write().cancel();
|
||||||
|
|
||||||
|
// Start a new task
|
||||||
|
task.set(cb.call());
|
||||||
|
}
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
AsyncMemo { task, value, state }
|
AsyncMemo { task, value, state }
|
||||||
@@ -52,8 +65,8 @@ where
|
|||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub struct AsyncMemo<T: 'static> {
|
pub struct AsyncMemo<T: 'static> {
|
||||||
value: Signal<Option<Signal<T>>>,
|
value: Signal<Option<Signal<T>>>,
|
||||||
task: Signal<Option<Task>>,
|
task: Signal<Task>,
|
||||||
state: Signal<UseResourceState<T>>,
|
state: Signal<UseResourceState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> AsyncMemo<T> {
|
impl<T> AsyncMemo<T> {
|
||||||
@@ -93,7 +106,7 @@ impl<T> AsyncMemo<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current state of the future.
|
/// Get the current state of the future.
|
||||||
pub fn state(&self) -> UseResourceState<T> {
|
pub fn state(&self) -> UseResourceState {
|
||||||
todo!()
|
todo!()
|
||||||
// match (&self.task.get(), &self.value()) {
|
// match (&self.task.get(), &self.value()) {
|
||||||
// // If we have a task and an existing value, we're reloading
|
// // If we have a task and an existing value, we're reloading
|
||||||
@@ -121,8 +134,8 @@ impl<T> AsyncMemo<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum UseResourceState<T: 'static> {
|
pub enum UseResourceState {
|
||||||
Pending,
|
Pending,
|
||||||
Complete(Signal<T>),
|
Complete,
|
||||||
Regenerating(Signal<T>), // the old value
|
Regenerating, // the old value
|
||||||
}
|
}
|
||||||
@@ -156,7 +156,7 @@ impl Effect {
|
|||||||
/// Create a new effect. The effect will be run immediately and whenever any signal it reads changes.
|
/// 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.
|
/// The signal will be owned by the current component and will be dropped when the component is dropped.
|
||||||
pub fn new(mut callback: impl FnMut() + 'static) -> Self {
|
fn new(mut callback: impl FnMut() + 'static) -> Self {
|
||||||
let source = current_scope_id().expect("in a virtual dom");
|
let source = current_scope_id().expect("in a virtual dom");
|
||||||
let myself = Self {
|
let myself = Self {
|
||||||
source,
|
source,
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ pub use read_only_signal::*;
|
|||||||
mod map;
|
mod map;
|
||||||
pub use map::*;
|
pub use map::*;
|
||||||
|
|
||||||
mod comparer;
|
// mod comparer;
|
||||||
pub use comparer::*;
|
// pub use comparer::*;
|
||||||
|
|
||||||
mod global;
|
mod global;
|
||||||
pub use global::*;
|
pub use global::*;
|
||||||
|
|||||||
+12
-22
@@ -5,7 +5,6 @@ use dioxus_core::prelude::{
|
|||||||
use generational_box::{GenerationalBoxId, SyncStorage};
|
use generational_box::{GenerationalBoxId, SyncStorage};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use std::{cell::RefCell, hash::Hash};
|
use std::{cell::RefCell, hash::Hash};
|
||||||
use tokio::signal;
|
|
||||||
|
|
||||||
use crate::{CopyValue, RcList, Readable, Writable};
|
use crate::{CopyValue, RcList, Readable, Writable};
|
||||||
|
|
||||||
@@ -37,17 +36,17 @@ impl ReactiveContext {
|
|||||||
signal_subscribers: FxHashMap::default(),
|
signal_subscribers: FxHashMap::default(),
|
||||||
scope_subscribers,
|
scope_subscribers,
|
||||||
sender: tx,
|
sender: tx,
|
||||||
_self: None,
|
self_: None,
|
||||||
receiver: rx,
|
receiver: rx,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut _self = Self {
|
let mut self_ = Self {
|
||||||
inner: CopyValue::new_maybe_sync(inner),
|
inner: CopyValue::new_maybe_sync(inner),
|
||||||
};
|
};
|
||||||
|
|
||||||
_self.inner.write()._self = Some(_self);
|
self_.inner.write().self_ = Some(self_);
|
||||||
|
|
||||||
_self
|
self_
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current reactive context
|
/// Get the current reactive context
|
||||||
@@ -60,24 +59,16 @@ impl ReactiveContext {
|
|||||||
|
|
||||||
// If we're already inside a reactive context, then return that
|
// If we're already inside a reactive context, then return that
|
||||||
if let Some(cur) = cur {
|
if let Some(cur) = cur {
|
||||||
println!("Already found context!");
|
|
||||||
return Some(cur);
|
return Some(cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try and get the context out of the current scope
|
|
||||||
let scope = current_scope_id().unwrap();
|
|
||||||
|
|
||||||
// If we're rendering, then try and use the reactive context attached to this component
|
// If we're rendering, then try and use the reactive context attached to this component
|
||||||
|
|
||||||
if let Some(cx) = has_context() {
|
if let Some(cx) = has_context() {
|
||||||
println!("found context at {scope:?}");
|
|
||||||
return Some(cx);
|
return Some(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("creating new context at {scope:?}");
|
|
||||||
|
|
||||||
// Otherwise, create a new context at the current scope
|
// Otherwise, create a new context at the current scope
|
||||||
Some(provide_context(ReactiveContext::new(Some(scope))))
|
Some(provide_context(ReactiveContext::new(current_scope_id())))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run this function in the context of this reactive context
|
/// Run this function in the context of this reactive context
|
||||||
@@ -96,7 +87,6 @@ impl ReactiveContext {
|
|||||||
/// If there's a scope associated with this context, then it will be marked as dirty too
|
/// If there's a scope associated with this context, then it will be marked as dirty too
|
||||||
pub fn mark_dirty(&self) {
|
pub fn mark_dirty(&self) {
|
||||||
for scope in self.inner.read().scope_subscribers.iter() {
|
for scope in self.inner.read().scope_subscribers.iter() {
|
||||||
println!("marking dirty {scope:?}");
|
|
||||||
needs_update_any(*scope);
|
needs_update_any(*scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +95,8 @@ impl ReactiveContext {
|
|||||||
_ = self.inner.read().sender.try_send(());
|
_ = self.inner.read().sender.try_send(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a two-way binding between this reactive context and a signal
|
/// Create a two-way binding between this reactive context and a signal
|
||||||
pub fn link(&self, signal: GenerationalBoxId, rc_list: RcList) {
|
pub fn link(&mut self, signal: GenerationalBoxId, rc_list: RcList) {
|
||||||
rc_list.write().insert(*self);
|
rc_list.write().insert(*self);
|
||||||
self.inner
|
self.inner
|
||||||
.write()
|
.write()
|
||||||
@@ -132,11 +122,11 @@ impl Hash for ReactiveContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Inner {
|
struct Inner {
|
||||||
// Set of signals bound to this context
|
// Set of signals bound to this context
|
||||||
pub signal_subscribers: FxHashMap<GenerationalBoxId, RcList>,
|
signal_subscribers: FxHashMap<GenerationalBoxId, RcList>,
|
||||||
scope_subscribers: FxHashSet<ScopeId>,
|
scope_subscribers: FxHashSet<ScopeId>,
|
||||||
_self: Option<ReactiveContext>,
|
self_: Option<ReactiveContext>,
|
||||||
|
|
||||||
// Futures will call .changed().await
|
// Futures will call .changed().await
|
||||||
sender: flume::Sender<()>,
|
sender: flume::Sender<()>,
|
||||||
@@ -144,10 +134,10 @@ pub struct Inner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Inner {
|
impl Drop for Inner {
|
||||||
|
// Remove this context from all the subscribers
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Remove this context from all the subscribers
|
|
||||||
self.signal_subscribers.values().for_each(|sub_list| {
|
self.signal_subscribers.values().for_each(|sub_list| {
|
||||||
sub_list.write().remove(&self._self.unwrap());
|
sub_list.write().remove(&self.self_.unwrap());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user