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};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
fn app() -> Element {
|
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 count = use_signal(|| 0);
|
||||||
let mut text = use_signal(|| "...".to_string());
|
let mut text = use_signal(|| "...".to_string());
|
||||||
|
|||||||
@@ -147,6 +147,11 @@ impl<T> Resource<T> {
|
|||||||
self.state.into()
|
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
|
/// Wait for this async memo to resolve, returning the inner signal value
|
||||||
/// If the value is pending, returns none and suspends the current component
|
/// If the value is pending, returns none and suspends the current component
|
||||||
pub fn suspend(&self) -> Option<ReadOnlySignal<T>> {
|
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;
|
mod rt;
|
||||||
pub use rt::*;
|
pub use rt::*;
|
||||||
|
|
||||||
mod effect;
|
|
||||||
pub use effect::*;
|
|
||||||
|
|
||||||
pub(crate) mod signal;
|
pub(crate) mod signal;
|
||||||
pub use signal::*;
|
pub use signal::*;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ thread_local! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ReactiveContext {
|
impl ReactiveContext {
|
||||||
|
/// Create a new reactive context
|
||||||
pub fn new(scope: Option<ScopeId>) -> Self {
|
pub fn new(scope: Option<ScopeId>) -> Self {
|
||||||
let (tx, rx) = flume::unbounded();
|
let (tx, rx) = flume::unbounded();
|
||||||
|
|
||||||
@@ -104,6 +105,11 @@ impl ReactiveContext {
|
|||||||
.insert(signal, rc_list);
|
.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
|
/// Wait for this reactive context to change
|
||||||
pub async fn changed(&self) {
|
pub async fn changed(&self) {
|
||||||
let rx = self.inner.read().receiver.clone();
|
let rx = self.inner.read().receiver.clone();
|
||||||
|
|||||||
+10
-25
@@ -13,9 +13,8 @@ use dioxus_core::ScopeId;
|
|||||||
|
|
||||||
use generational_box::{GenerationalBox, Owner, Storage};
|
use generational_box::{GenerationalBox, Owner, Storage};
|
||||||
|
|
||||||
use crate::Effect;
|
|
||||||
use crate::Readable;
|
|
||||||
use crate::Writable;
|
use crate::Writable;
|
||||||
|
use crate::{ReactiveContext, Readable};
|
||||||
|
|
||||||
/// Run a closure with the given owner.
|
/// Run a closure with the given owner.
|
||||||
pub fn with_owner<S: AnyStorage, F: FnOnce() -> R, R>(owner: Owner<S>, f: F) -> R {
|
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;
|
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.
|
||||||
// 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() {
|
||||||
Some(effect) => {
|
return owner_in_scope(scope.origin_scope());
|
||||||
let scope_id = effect.source;
|
}
|
||||||
owner_in_scope(scope_id)
|
|
||||||
}
|
// Otherwise either get an owner from the current scope or create a new one.
|
||||||
// Otherwise either get an owner from the current scope or create a new one.
|
match has_context() {
|
||||||
None => match has_context() {
|
Some(rt) => rt,
|
||||||
Some(rt) => rt,
|
None => provide_context(S::owner()),
|
||||||
None => {
|
|
||||||
let owner = S::owner();
|
|
||||||
provide_context(owner)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,15 +200,6 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
|
|||||||
.expect("value is already dropped or borrowed")
|
.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.
|
/// Get the scope this value was created in.
|
||||||
pub fn origin_scope(&self) -> ScopeId {
|
pub fn origin_scope(&self) -> ScopeId {
|
||||||
self.origin_scope
|
self.origin_scope
|
||||||
|
|||||||
@@ -1,23 +1,17 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
get_effect_ref, read::Readable, write::Writable, CopyValue, Effect, EffectInner,
|
read::Readable, write::Writable, CopyValue, GlobalMemo, GlobalSignal, MappedSignal,
|
||||||
EffectStackRef, GlobalMemo, GlobalSignal, MappedSignal, ReactiveContext, ReadOnlySignal,
|
ReactiveContext, ReadOnlySignal,
|
||||||
EFFECT_STACK,
|
|
||||||
};
|
};
|
||||||
use dioxus_core::{
|
use dioxus_core::{
|
||||||
prelude::{
|
prelude::{flush_sync, spawn, IntoAttributeValue},
|
||||||
current_scope_id, flush_sync, has_context, provide_context, schedule_update_any, spawn,
|
|
||||||
IntoAttributeValue,
|
|
||||||
},
|
|
||||||
ScopeId,
|
ScopeId,
|
||||||
};
|
};
|
||||||
use generational_box::{AnyStorage, GenerationalBoxId, Storage, SyncStorage, UnsyncStorage};
|
use generational_box::{AnyStorage, Storage, SyncStorage, UnsyncStorage};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
cell::RefCell,
|
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
rc::Rc,
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user