Fix warnings and compile errors
This commit is contained in:
7 files changed
+26
-241
No files matched your search
@@ -10,7 +10,7 @@ use dioxus_fullstack::{launch, prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
fn app() -> Element {
|
||||
let state = use_server_future(move || async move { get_server_data().await.unwrap() });
|
||||
let state = use_server_future(move || async move { get_server_data().await.unwrap() })?;
|
||||
|
||||
let mut count = use_signal(|| 0);
|
||||
let mut text = use_signal(|| "...".to_string());
|
||||
|
||||
@@ -147,6 +147,11 @@ impl<T> Resource<T> {
|
||||
self.state.into()
|
||||
}
|
||||
|
||||
/// Get the current value of the future.
|
||||
pub fn value(&self) -> Option<ReadOnlySignal<T>> {
|
||||
self.value.peek().as_ref().map(|sig| sig.clone().into())
|
||||
}
|
||||
|
||||
/// Wait for this async memo to resolve, returning the inner signal value
|
||||
/// If the value is pending, returns none and suspends the current component
|
||||
pub fn suspend(&self) -> Option<ReadOnlySignal<T>> {
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
use crate::write::*;
|
||||
use crate::CopyValue;
|
||||
use core::{self, fmt::Debug};
|
||||
use dioxus_core::prelude::*;
|
||||
use futures_channel::mpsc::UnboundedSender;
|
||||
use futures_util::{future::Either, pin_mut, StreamExt};
|
||||
use generational_box::GenerationalBoxId;
|
||||
use parking_lot::RwLock;
|
||||
use rustc_hash::FxHashMap;
|
||||
use std::fmt::{self, Formatter};
|
||||
|
||||
thread_local! {
|
||||
pub(crate)static EFFECT_STACK: EffectStack = EffectStack::default();
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct EffectStack {
|
||||
pub(crate) effects: RwLock<Vec<Effect>>,
|
||||
pub(crate) effect_mapping: RwLock<FxHashMap<GenerationalBoxId, Effect>>,
|
||||
}
|
||||
|
||||
impl EffectStack {
|
||||
pub(crate) fn current(&self) -> Option<Effect> {
|
||||
self.effects.read().last().copied()
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a thread safe reference to an effect stack running on another thread.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct EffectStackRef {
|
||||
rerun_effect: UnboundedSender<GenerationalBoxId>,
|
||||
}
|
||||
|
||||
impl EffectStackRef {
|
||||
pub(crate) fn rerun_effect(&self, id: GenerationalBoxId) {
|
||||
self.rerun_effect.unbounded_send(id).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_effect_ref() -> EffectStackRef {
|
||||
if let Some(rt) = try_consume_context() {
|
||||
return rt;
|
||||
}
|
||||
|
||||
let (sender, receiver) = futures_channel::mpsc::unbounded();
|
||||
|
||||
spawn_forever(async move { effect_driver(receiver).await });
|
||||
|
||||
let stack_ref = EffectStackRef {
|
||||
rerun_effect: sender,
|
||||
};
|
||||
|
||||
provide_root_context(stack_ref.clone());
|
||||
|
||||
stack_ref
|
||||
}
|
||||
|
||||
/// The primary top-level driver of all effects
|
||||
///
|
||||
/// In Dioxus, effects are neither react effects nor solidjs effects. They are a hybrid of the two, making our model
|
||||
/// more complex but also more powerful.
|
||||
///
|
||||
/// In react, when a component renders, it can queue up effects to be run after the component is done rendering.
|
||||
/// This is done *only during render* and determined by the dependency array attached to the effect. In Dioxus,
|
||||
/// we track effects using signals, so these effects can actually run multiple times after the component has rendered.
|
||||
///
|
||||
///
|
||||
async fn effect_driver(
|
||||
mut receiver: futures_channel::mpsc::UnboundedReceiver<GenerationalBoxId>,
|
||||
) -> ! {
|
||||
let mut queued_memos = Vec::new();
|
||||
|
||||
loop {
|
||||
// Wait for a flush
|
||||
// This gives a chance for effects to be updated in place and memos to compute their values
|
||||
let flush_await = flush_sync();
|
||||
pin_mut!(flush_await);
|
||||
|
||||
// Until the flush is ready, wait for a new effect to be queued
|
||||
// We don't run the effects immediately because we want to batch them on the next call to flush
|
||||
// todo: the queued memos should be unqueued when components are dropped
|
||||
loop {
|
||||
match futures_util::future::select(&mut flush_await, receiver.next()).await {
|
||||
// VDOM is flushed and we can run the queued effects
|
||||
Either::Left(_flushed) => break,
|
||||
|
||||
// A new effect was queued to be run after the next flush
|
||||
// Marking components as dirty is handled syncrhonously on write, though we could try
|
||||
// batching them here too
|
||||
Either::Right((_queued, _)) => {
|
||||
if let Some(task) = _queued {
|
||||
queued_memos.push(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EFFECT_STACK.with(|stack| {
|
||||
for id in queued_memos.drain(..) {
|
||||
let effect_mapping = stack.effect_mapping.read();
|
||||
if let Some(mut effect) = effect_mapping.get(&id).copied() {
|
||||
tracing::trace!("Rerunning effect: {:?}", id);
|
||||
effect.try_run();
|
||||
} else {
|
||||
tracing::trace!("Effect not found: {:?}", id);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
pub struct Effect {
|
||||
pub(crate) source: ScopeId,
|
||||
pub(crate) inner: CopyValue<EffectInner>,
|
||||
}
|
||||
|
||||
pub(crate) struct EffectInner {
|
||||
pub(crate) callback: Box<dyn FnMut()>,
|
||||
pub(crate) id: GenerationalBoxId,
|
||||
}
|
||||
|
||||
impl EffectInner {
|
||||
pub(crate) fn new(callback: Box<dyn FnMut()>) -> CopyValue<Self> {
|
||||
let mut copy = CopyValue::invalid();
|
||||
let inner = EffectInner {
|
||||
callback: Box::new(callback),
|
||||
id: copy.id(),
|
||||
};
|
||||
copy.set(inner);
|
||||
copy
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EffectInner {
|
||||
fn drop(&mut self) {
|
||||
EFFECT_STACK.with(|stack| {
|
||||
tracing::trace!("Dropping effect: {:?}", self.id);
|
||||
stack.effect_mapping.write().remove(&self.id);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Effect {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.write_fmt(format_args!("{:?}", self.inner.value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Effect {
|
||||
pub(crate) fn current() -> Option<Self> {
|
||||
EFFECT_STACK.with(|stack| stack.effects.read().last().copied())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
fn new(mut callback: impl FnMut() + 'static) -> Self {
|
||||
let source = current_scope_id().expect("in a virtual dom");
|
||||
let myself = Self {
|
||||
source,
|
||||
#[allow(clippy::redundant_closure)]
|
||||
inner: EffectInner::new(Box::new(move || source.in_runtime(|| callback()))),
|
||||
};
|
||||
|
||||
EFFECT_STACK.with(|stack| {
|
||||
stack
|
||||
.effect_mapping
|
||||
.write()
|
||||
.insert(myself.inner.id(), myself);
|
||||
});
|
||||
tracing::trace!("Created effect: {:?}", myself);
|
||||
|
||||
get_effect_ref().rerun_effect(myself.inner.id());
|
||||
|
||||
myself
|
||||
}
|
||||
|
||||
/// Run the effect callback immediately. Returns `true` if the effect was run. Returns `false` is the effect is dead.
|
||||
pub fn try_run(&mut self) {
|
||||
tracing::trace!("Running effect: {:?}", self);
|
||||
if let Ok(mut inner) = self.inner.try_write() {
|
||||
{
|
||||
EFFECT_STACK.with(|stack| {
|
||||
stack.effects.write().push(*self);
|
||||
});
|
||||
}
|
||||
(inner.callback)();
|
||||
{
|
||||
EFFECT_STACK.with(|stack| {
|
||||
stack.effects.write().pop();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the id of this effect.
|
||||
pub fn id(&self) -> GenerationalBoxId {
|
||||
self.inner.id()
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,6 @@
|
||||
mod rt;
|
||||
pub use rt::*;
|
||||
|
||||
mod effect;
|
||||
pub use effect::*;
|
||||
|
||||
pub(crate) mod signal;
|
||||
pub use signal::*;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ thread_local! {
|
||||
}
|
||||
|
||||
impl ReactiveContext {
|
||||
/// Create a new reactive context
|
||||
pub fn new(scope: Option<ScopeId>) -> Self {
|
||||
let (tx, rx) = flume::unbounded();
|
||||
|
||||
@@ -104,6 +105,11 @@ impl ReactiveContext {
|
||||
.insert(signal, rc_list);
|
||||
}
|
||||
|
||||
/// Get the scope that inner CopyValue is associated with
|
||||
pub fn origin_scope(&self) -> ScopeId {
|
||||
self.inner.origin_scope()
|
||||
}
|
||||
|
||||
/// Wait for this reactive context to change
|
||||
pub async fn changed(&self) {
|
||||
let rx = self.inner.read().receiver.clone();
|
||||
|
||||
+10
-25
@@ -13,9 +13,8 @@ use dioxus_core::ScopeId;
|
||||
|
||||
use generational_box::{GenerationalBox, Owner, Storage};
|
||||
|
||||
use crate::Effect;
|
||||
use crate::Readable;
|
||||
use crate::Writable;
|
||||
use crate::{ReactiveContext, Readable};
|
||||
|
||||
/// Run a closure with the given owner.
|
||||
pub fn with_owner<S: AnyStorage, F: FnOnce() -> R, R>(owner: Owner<S>, f: F) -> R {
|
||||
@@ -85,20 +84,15 @@ fn current_owner<S: Storage<T>, T>() -> Owner<S> {
|
||||
return owner;
|
||||
}
|
||||
|
||||
match Effect::current() {
|
||||
// If we are inside of an effect, we should use the owner of the effect as the owner of the value.
|
||||
Some(effect) => {
|
||||
let scope_id = effect.source;
|
||||
owner_in_scope(scope_id)
|
||||
}
|
||||
// Otherwise either get an owner from the current scope or create a new one.
|
||||
None => match has_context() {
|
||||
Some(rt) => rt,
|
||||
None => {
|
||||
let owner = S::owner();
|
||||
provide_context(owner)
|
||||
}
|
||||
},
|
||||
// If we are inside of an effect, we should use the owner of the effect as the owner of the value.
|
||||
if let Some(scope) = ReactiveContext::current() {
|
||||
return owner_in_scope(scope.origin_scope());
|
||||
}
|
||||
|
||||
// Otherwise either get an owner from the current scope or create a new one.
|
||||
match has_context() {
|
||||
Some(rt) => rt,
|
||||
None => provide_context(S::owner()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,15 +200,6 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
|
||||
.expect("value is already dropped or borrowed")
|
||||
}
|
||||
|
||||
pub(crate) fn invalid() -> Self {
|
||||
let owner = current_owner();
|
||||
|
||||
Self {
|
||||
value: owner.invalid(),
|
||||
origin_scope: current_scope_id().expect("in a virtual dom"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the scope this value was created in.
|
||||
pub fn origin_scope(&self) -> ScopeId {
|
||||
self.origin_scope
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
use crate::{
|
||||
get_effect_ref, read::Readable, write::Writable, CopyValue, Effect, EffectInner,
|
||||
EffectStackRef, GlobalMemo, GlobalSignal, MappedSignal, ReactiveContext, ReadOnlySignal,
|
||||
EFFECT_STACK,
|
||||
read::Readable, write::Writable, CopyValue, GlobalMemo, GlobalSignal, MappedSignal,
|
||||
ReactiveContext, ReadOnlySignal,
|
||||
};
|
||||
use dioxus_core::{
|
||||
prelude::{
|
||||
current_scope_id, flush_sync, has_context, provide_context, schedule_update_any, spawn,
|
||||
IntoAttributeValue,
|
||||
},
|
||||
prelude::{flush_sync, spawn, IntoAttributeValue},
|
||||
ScopeId,
|
||||
};
|
||||
use generational_box::{AnyStorage, GenerationalBoxId, Storage, SyncStorage, UnsyncStorage};
|
||||
use generational_box::{AnyStorage, Storage, SyncStorage, UnsyncStorage};
|
||||
use parking_lot::RwLock;
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::RefCell,
|
||||
collections::HashSet,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user