mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 06:34:20 +00:00
implement use_async_memo with reactive context
This commit is contained in:
parent
7c2947a131
commit
dd06705ff1
7 changed files with 68 additions and 58 deletions
|
@ -1,3 +1,5 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
|
@ -23,7 +25,17 @@ fn app() -> Element {
|
|||
fn Child(sig: Signal<i32>) -> Element {
|
||||
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! {
|
||||
"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 dioxus_core::prelude::*;
|
||||
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.
|
||||
/// 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 {
|
||||
loop {
|
||||
rc.changed().await;
|
||||
|
||||
println!("change triggered in memo");
|
||||
|
||||
let new = rc.run_in(|| f());
|
||||
|
||||
if new != *state.peek() {
|
||||
println!("change sett in memo");
|
||||
*state.write() = new;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#![allow(missing_docs)]
|
||||
|
||||
use crate::use_signal;
|
||||
use crate::{use_callback, use_signal};
|
||||
use dioxus_core::{
|
||||
prelude::{spawn, suspend},
|
||||
prelude::{spawn, suspend, use_hook},
|
||||
Task,
|
||||
};
|
||||
use dioxus_signals::*;
|
||||
|
@ -19,31 +19,44 @@ where
|
|||
F: Future<Output = T> + 'static,
|
||||
{
|
||||
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
|
||||
let fut = future();
|
||||
let fut = rc.run_in(|| 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 poll it
|
||||
// let fut = fut;
|
||||
// pin_mut!(fut);
|
||||
spawn(async move {
|
||||
// move the future here and pin it so we can poll it
|
||||
let fut = fut;
|
||||
pin_mut!(fut);
|
||||
|
||||
// let res = future::poll_fn(|cx| {
|
||||
// // Set the effect stack properly
|
||||
// // add any dependencies to the effect stack that we need to watch when restarting the future
|
||||
// // Poll the inner future
|
||||
// fut.poll_unpin(cx)
|
||||
// })
|
||||
// .await;
|
||||
// Run each poll in the context of the reactive scope
|
||||
// This ensures the scope is properly subscribed to the future's dependencies
|
||||
let res = future::poll_fn(|cx| rc.run_in(|| fut.poll_unpin(cx))).await;
|
||||
|
||||
// // Set the value
|
||||
// value.set(Some(Signal::new(res)));
|
||||
});
|
||||
// Set the value and state
|
||||
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 }
|
||||
|
@ -52,8 +65,8 @@ where
|
|||
#[allow(unused)]
|
||||
pub struct AsyncMemo<T: 'static> {
|
||||
value: Signal<Option<Signal<T>>>,
|
||||
task: Signal<Option<Task>>,
|
||||
state: Signal<UseResourceState<T>>,
|
||||
task: Signal<Task>,
|
||||
state: Signal<UseResourceState>,
|
||||
}
|
||||
|
||||
impl<T> AsyncMemo<T> {
|
||||
|
@ -93,7 +106,7 @@ impl<T> AsyncMemo<T> {
|
|||
}
|
||||
|
||||
/// Get the current state of the future.
|
||||
pub fn state(&self) -> UseResourceState<T> {
|
||||
pub fn state(&self) -> UseResourceState {
|
||||
todo!()
|
||||
// match (&self.task.get(), &self.value()) {
|
||||
// // 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,
|
||||
Complete(Signal<T>),
|
||||
Regenerating(Signal<T>), // the old value
|
||||
Complete,
|
||||
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.
|
||||
///
|
||||
/// 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 myself = Self {
|
||||
source,
|
||||
|
|
|
@ -19,8 +19,8 @@ pub use read_only_signal::*;
|
|||
mod map;
|
||||
pub use map::*;
|
||||
|
||||
mod comparer;
|
||||
pub use comparer::*;
|
||||
// mod comparer;
|
||||
// pub use comparer::*;
|
||||
|
||||
mod global;
|
||||
pub use global::*;
|
||||
|
|
|
@ -5,7 +5,6 @@ use dioxus_core::prelude::{
|
|||
use generational_box::{GenerationalBoxId, SyncStorage};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use std::{cell::RefCell, hash::Hash};
|
||||
use tokio::signal;
|
||||
|
||||
use crate::{CopyValue, RcList, Readable, Writable};
|
||||
|
||||
|
@ -37,17 +36,17 @@ impl ReactiveContext {
|
|||
signal_subscribers: FxHashMap::default(),
|
||||
scope_subscribers,
|
||||
sender: tx,
|
||||
_self: None,
|
||||
self_: None,
|
||||
receiver: rx,
|
||||
};
|
||||
|
||||
let mut _self = Self {
|
||||
let mut self_ = Self {
|
||||
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
|
||||
|
@ -60,24 +59,16 @@ impl ReactiveContext {
|
|||
|
||||
// If we're already inside a reactive context, then return that
|
||||
if let Some(cur) = cur {
|
||||
println!("Already found context!");
|
||||
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 let Some(cx) = has_context() {
|
||||
println!("found context at {scope:?}");
|
||||
return Some(cx);
|
||||
}
|
||||
|
||||
println!("creating new context at {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
|
||||
|
@ -96,7 +87,6 @@ impl ReactiveContext {
|
|||
/// If there's a scope associated with this context, then it will be marked as dirty too
|
||||
pub fn mark_dirty(&self) {
|
||||
for scope in self.inner.read().scope_subscribers.iter() {
|
||||
println!("marking dirty {scope:?}");
|
||||
needs_update_any(*scope);
|
||||
}
|
||||
|
||||
|
@ -105,8 +95,8 @@ impl ReactiveContext {
|
|||
_ = self.inner.read().sender.try_send(());
|
||||
}
|
||||
|
||||
// Create a two-way binding between this reactive context and a signal
|
||||
pub fn link(&self, signal: GenerationalBoxId, rc_list: RcList) {
|
||||
/// Create a two-way binding between this reactive context and a signal
|
||||
pub fn link(&mut self, signal: GenerationalBoxId, rc_list: RcList) {
|
||||
rc_list.write().insert(*self);
|
||||
self.inner
|
||||
.write()
|
||||
|
@ -132,11 +122,11 @@ impl Hash for ReactiveContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Inner {
|
||||
struct Inner {
|
||||
// Set of signals bound to this context
|
||||
pub signal_subscribers: FxHashMap<GenerationalBoxId, RcList>,
|
||||
signal_subscribers: FxHashMap<GenerationalBoxId, RcList>,
|
||||
scope_subscribers: FxHashSet<ScopeId>,
|
||||
_self: Option<ReactiveContext>,
|
||||
self_: Option<ReactiveContext>,
|
||||
|
||||
// Futures will call .changed().await
|
||||
sender: flume::Sender<()>,
|
||||
|
@ -144,10 +134,10 @@ pub struct Inner {
|
|||
}
|
||||
|
||||
impl Drop for Inner {
|
||||
// Remove this context from all the subscribers
|
||||
fn drop(&mut self) {
|
||||
// Remove this context from all the subscribers
|
||||
self.signal_subscribers.values().for_each(|sub_list| {
|
||||
sub_list.write().remove(&self._self.unwrap());
|
||||
sub_list.write().remove(&self.self_.unwrap());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue