Fix warnings and compile errors

This commit is contained in:
Jonathan Kelley committed 2024-02-03 13:10:16 -08:00
1 parent e767e49d5e
commit 7d78e5e250
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());
+5
View File
@@ -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>> {
-202
View File
@@ -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()
}
}
-3
View File
@@ -7,9 +7,6 @@
mod rt;
pub use rt::*;
mod effect;
pub use effect::*;
pub(crate) mod signal;
pub use signal::*;
+6
View File
@@ -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
View File
@@ -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
+4 -10
View File
@@ -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,
};