Merge branch 'master' into debug-signals

This commit is contained in:
Evan Almloff committed 2023-09-22 07:59:47 -05:00
commit 78860da006
434 files changed
+2673 -14238

No files matched your search

+1 -1
View File
@@ -9,7 +9,7 @@ edition = "2018"
[dependencies]
dioxus-core = { workspace = true }
generational-box = { workspace = true }
log.workspace = true
tracing = { workspace = true }
simple_logger = "4.2.0"
serde = { version = "1", features = ["derive"], optional = true }
+13 -7
View File
@@ -10,7 +10,8 @@ All signals implement Copy, even if the inner value does not implement copy. Thi
use dioxus::prelude::*;
use dioxus_signals::*;
fn app(cx: Scope) -> Element {
#[component]
fn App(cx: Scope) -> Element {
let signal = use_signal(cx, || "hello world".to_string());
spawn(async move {
@@ -32,7 +33,8 @@ Signals will only subscribe to components when you read from the signal in that
use dioxus::prelude::*;
use dioxus_signals::*;
fn app(cx: Scope) -> Element {
#[component]
fn App(cx: Scope) -> Element {
// Because signal is never read in this component, this component will not rerun when the signal changes
let signal = use_signal(cx, || 0);
@@ -56,6 +58,7 @@ struct ChildProps {
signal: Signal<usize>,
}
#[component]
fn Child(cx: Scope<ChildProps>) -> Element {
// This component does read from the signal, so when the signal changes it will rerun
render! {
@@ -70,15 +73,17 @@ Because subscriptions happen when you read from (not create) the data, you can p
use dioxus::prelude::*;
use dioxus_signals::*;
fn app(cx: Scope) -> Element {
#[component]
fn App(cx: Scope) -> Element {
// Because signal is never read in this component, this component will not rerun when the signal changes
use_context_provider(cx, || Signal::new(0));
render! {
Child {}
}
}
#[component]
fn Child(cx: Scope) -> Element {
let signal: Signal<i32> = *use_context(cx).unwrap();
// This component does read from the signal, so when the signal changes it will rerun
@@ -92,13 +97,14 @@ fn Child(cx: Scope) -> Element {
In addition to local subscriptions in components, `dioxus-signals` provides a way to derive data with local subscriptions.
The use_selector hook will only rerun when any signals inside of the hook change:
The use_selector hook will only rerun when any signals inside the hook change:
```rust
use dioxus::prelude::*;
use dioxus_signals::*;
fn app(cx: Scope) -> Element {
#[component]
fn App(cx: Scope) -> Element {
let signal = use_signal(cx, || 0);
let doubled = use_selector(cx, || signal * 2);
@@ -113,7 +119,7 @@ fn app(cx: Scope) -> Element {
}
}
#[inline_props]
#[component]
fn Child(cx: Scope, signal: ReadOnlySignal<usize>) -> Element {
render! {
"{signal}"
+4 -5
View File
@@ -1,13 +1,12 @@
#![allow(non_snake_case)]
use dioxus::prelude::*;
use dioxus_signals::*;
fn main() {
dioxus_desktop::launch(app);
dioxus_desktop::launch(App);
}
fn app(cx: Scope) -> Element {
#[component]
fn App(cx: Scope) -> Element {
let signal = use_signal(cx, || 0);
let doubled = use_selector(cx, move || signal * 2);
@@ -22,7 +21,7 @@ fn app(cx: Scope) -> Element {
}
}
#[inline_props]
#[component]
fn Child(cx: Scope, signal: ReadOnlySignal<usize>) -> Element {
render! {
"{signal}"
+4 -1
View File
@@ -18,7 +18,8 @@ pub(crate) fn get_effect_stack() -> EffectStack {
Some(rt) => rt,
None => {
let store = EffectStack::default();
provide_root_context(store).expect("in a virtual dom")
provide_root_context(store.clone());
store
}
}
}
@@ -54,6 +55,7 @@ pub fn use_effect_with_dependencies<D: Dependency>(
/// 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) callback: CopyValue<Box<dyn FnMut()>>,
}
@@ -73,6 +75,7 @@ impl Effect {
/// The signal will be owned by the current component and will be dropped when the component is dropped.
pub fn new(callback: impl FnMut() + 'static) -> Self {
let myself = Self {
source: current_scope_id().expect("in a virtual dom"),
callback: CopyValue::new(Box::new(callback)),
};
+16 -9
View File
@@ -1,15 +1,14 @@
use std::rc::Rc;
use dioxus_core::prelude::{
consume_context, consume_context_from_scope, current_scope_id, has_context, provide_context,
provide_context_to_scope, provide_root_context,
};
use dioxus_core::prelude::*;
use dioxus_core::ScopeId;
use generational_box::{
BorrowError, BorrowMutError, GenerationalBox, GenerationalRef, GenerationalRefMut, Owner, Store,
};
use crate::Effect;
fn current_store() -> Store {
match consume_context() {
Some(rt) => rt,
@@ -21,12 +20,20 @@ fn current_store() -> Store {
}
fn current_owner() -> Rc<Owner> {
match has_context() {
Some(rt) => rt,
None => {
let owner = Rc::new(current_store().owner());
provide_context(owner).expect("in a virtual dom")
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 = Rc::new(current_store().owner());
provide_context(owner).expect("in a virtual dom")
}
},
}
}
+1
View File
@@ -74,6 +74,7 @@ pub fn selector<R: PartialEq>(mut f: impl FnMut() -> R + 'static) -> ReadOnlySig
inner: CopyValue::invalid(),
};
let effect = Effect {
source: current_scope_id().expect("in a virtual dom"),
callback: CopyValue::invalid(),
};
+21 -5
View File
@@ -27,7 +27,7 @@ use crate::{CopyValue, Effect};
/// render! { Child { state: count } }
/// }
///
/// #[inline_props]
/// #[component]
/// fn Child(cx: Scope, state: Signal<u32>) -> Element {
/// let state = *state;
///
@@ -100,6 +100,7 @@ pub(crate) struct SignalData<T> {
/// use dioxus::prelude::*;
/// use dioxus_signals::*;
///
/// #[component]
/// fn App(cx: Scope) -> Element {
/// let mut count = use_signal(cx, || 0);
///
@@ -108,7 +109,7 @@ pub(crate) struct SignalData<T> {
/// render! { Child { state: count } }
/// }
///
/// #[inline_props]
/// #[component]
/// fn Child(cx: Scope, state: Signal<u32>) -> Element {
/// let state = *state;
///
@@ -176,6 +177,21 @@ impl<T: 'static> Signal<T> {
}
}
/// Create a new signal with a custom owner scope. The signal will be dropped when the owner scope is dropped instead of the current scope.
pub fn new_in_scope(value: T, owner: ScopeId) -> Self {
Self {
inner: CopyValue::new_in_scope(
SignalData {
subscribers: Default::default(),
effect_subscribers: Default::default(),
update_any: schedule_update_any().expect("in a virtual dom"),
value,
},
owner,
),
}
}
/// Get the scope the signal was created in.
pub fn origin_scope(&self) -> ScopeId {
self.inner.origin_scope()
@@ -194,7 +210,7 @@ impl<T: 'static> Signal<T> {
} else if let Some(current_scope_id) = current_scope_id() {
// only subscribe if the vdom is rendering
if dioxus_core::vdom_is_rendering() {
log::trace!(
tracing::trace!(
"{:?} subscribed to {:?}",
self.inner.value,
current_scope_id
@@ -227,7 +243,7 @@ impl<T: 'static> Signal<T> {
{
let inner = self.inner.read();
for &scope_id in &*inner.subscribers.borrow() {
log::trace!(
tracing::trace!(
"Write on {:?} triggered update on {:?}",
self.inner.value,
scope_id
@@ -242,7 +258,7 @@ impl<T: 'static> Signal<T> {
std::mem::take(&mut *effects)
};
for effect in subscribers {
log::trace!(
tracing::trace!(
"Write on {:?} triggered effect {:?}",
self.inner.value,
effect
+1 -1
View File
@@ -51,7 +51,7 @@ fn drop_signals() {
}
let _ = dom.rebuild().santize();
dom.mark_dirty(ScopeId(0));
dom.mark_dirty(ScopeId::ROOT);
dom.render_immediate();
fn create_without_cx() -> Signal<String> {
+2 -2
View File
@@ -124,7 +124,7 @@ fn memos_prevents_component_rerun() {
}
let _ = dom.rebuild().santize();
dom.mark_dirty(ScopeId(0));
dom.mark_dirty(ScopeId::ROOT);
dom.render_immediate();
{
@@ -133,7 +133,7 @@ fn memos_prevents_component_rerun() {
assert_eq!(current_counter.effect, 2);
}
dom.mark_dirty(ScopeId(0));
dom.mark_dirty(ScopeId::ROOT);
dom.render_immediate();
dom.render_immediate();
+1 -1
View File
@@ -77,7 +77,7 @@ fn reading_subscribes() {
}
}
dom.mark_dirty(ScopeId(0));
dom.mark_dirty(ScopeId::ROOT);
dom.render_immediate();
dom.render_immediate();