mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
Move Scope to first argument of hooks, rather than impl Scope, so that future hooks defined in other crates have isomorphic API (they can't arbitrarily impl Scope)
This commit is contained in:
parent
329eab89d2
commit
02558c2f66
9 changed files with 147 additions and 155 deletions
|
@ -2,22 +2,22 @@ use crate::{Runtime, Scope, ScopeId, Source, Subscriber};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::{any::type_name, cell::RefCell, collections::HashSet, fmt::Debug, marker::PhantomData};
|
||||
|
||||
pub fn create_render_effect<T>(cx: Scope, f: impl FnMut(Option<T>) -> T + 'static) -> Effect<T>
|
||||
where
|
||||
T: Debug + 'static,
|
||||
{
|
||||
cx.create_eff(true, f)
|
||||
}
|
||||
|
||||
pub fn create_effect<T>(cx: Scope, f: impl FnMut(Option<T>) -> T + 'static) -> Effect<T>
|
||||
where
|
||||
T: Debug + 'static,
|
||||
{
|
||||
cx.create_eff(false, f)
|
||||
}
|
||||
|
||||
impl Scope {
|
||||
pub fn create_render_effect<T>(self, f: impl FnMut(Option<T>) -> T + 'static) -> Effect<T>
|
||||
where
|
||||
T: Debug + 'static,
|
||||
{
|
||||
self.create_eff(true, f)
|
||||
}
|
||||
|
||||
pub fn create_effect<T>(self, f: impl FnMut(Option<T>) -> T + 'static) -> Effect<T>
|
||||
where
|
||||
T: Debug + 'static,
|
||||
{
|
||||
self.create_eff(false, f)
|
||||
}
|
||||
|
||||
fn create_eff<T>(
|
||||
pub(crate) fn create_eff<T>(
|
||||
self,
|
||||
render_effect: bool,
|
||||
f: impl FnMut(Option<T>) -> T + 'static,
|
||||
|
|
|
@ -9,27 +9,25 @@ use std::{
|
|||
marker::PhantomData,
|
||||
};
|
||||
|
||||
impl Scope {
|
||||
pub fn create_memo<T>(self, f: impl FnMut(Option<&T>) -> T + 'static) -> Memo<T>
|
||||
where
|
||||
T: Debug + 'static,
|
||||
{
|
||||
let state = MemoState::new(self.runtime, f);
|
||||
pub fn create_memo<T>(cx: Scope, f: impl FnMut(Option<&T>) -> T + 'static) -> Memo<T>
|
||||
where
|
||||
T: Debug + 'static,
|
||||
{
|
||||
let state = MemoState::new(cx.runtime, f);
|
||||
|
||||
let id = self.push_memo(state);
|
||||
let id = cx.push_memo(state);
|
||||
|
||||
let eff = Memo {
|
||||
runtime: self.runtime,
|
||||
scope: self.id,
|
||||
id,
|
||||
ty: PhantomData,
|
||||
};
|
||||
let eff = Memo {
|
||||
runtime: cx.runtime,
|
||||
scope: cx.id,
|
||||
id,
|
||||
ty: PhantomData,
|
||||
};
|
||||
|
||||
self.runtime
|
||||
.any_memo((self.id, id), |memo| memo.run((self.id, id)));
|
||||
cx.runtime
|
||||
.any_memo((cx.id, id), |memo| memo.run((cx.id, id)));
|
||||
|
||||
eff
|
||||
}
|
||||
eff
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -10,75 +10,73 @@ use std::{
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
queue_microtask, runtime::Runtime, spawn::spawn_local, Memo, ReadSignal, Scope, ScopeId,
|
||||
SignalId, SuspenseContext, WriteSignal,
|
||||
create_effect, create_memo, create_signal, queue_microtask, runtime::Runtime,
|
||||
spawn::spawn_local, Memo, ReadSignal, Scope, ScopeId, SuspenseContext, WriteSignal,
|
||||
};
|
||||
|
||||
impl Scope {
|
||||
pub fn create_resource<S, T, Fu>(
|
||||
self,
|
||||
source: ReadSignal<S>,
|
||||
fetcher: impl Fn(&S) -> Fu + 'static,
|
||||
) -> Resource<S, T, Fu>
|
||||
where
|
||||
S: Debug + Clone + 'static,
|
||||
T: Debug + Clone + 'static,
|
||||
Fu: Future<Output = T> + 'static,
|
||||
{
|
||||
self.create_resource_with_initial_value(source, fetcher, None)
|
||||
}
|
||||
pub fn create_resource<S, T, Fu>(
|
||||
cx: Scope,
|
||||
source: ReadSignal<S>,
|
||||
fetcher: impl Fn(&S) -> Fu + 'static,
|
||||
) -> Resource<S, T, Fu>
|
||||
where
|
||||
S: Debug + Clone + 'static,
|
||||
T: Debug + Clone + 'static,
|
||||
Fu: Future<Output = T> + 'static,
|
||||
{
|
||||
create_resource_with_initial_value(cx, source, fetcher, None)
|
||||
}
|
||||
|
||||
pub fn create_resource_with_initial_value<S, T, Fu>(
|
||||
self,
|
||||
source: ReadSignal<S>,
|
||||
fetcher: impl Fn(&S) -> Fu + 'static,
|
||||
initial_value: Option<T>,
|
||||
) -> Resource<S, T, Fu>
|
||||
where
|
||||
S: Debug + Clone + 'static,
|
||||
T: Debug + Clone + 'static,
|
||||
Fu: Future<Output = T> + 'static,
|
||||
{
|
||||
let resolved = initial_value.is_some();
|
||||
let (value, set_value) = self.create_signal(initial_value);
|
||||
let (loading, set_loading) = self.create_signal(false);
|
||||
let (track, trigger) = self.create_signal(0);
|
||||
let fetcher = Rc::new(fetcher);
|
||||
let source = self.create_memo(move |_| source());
|
||||
pub fn create_resource_with_initial_value<S, T, Fu>(
|
||||
cx: Scope,
|
||||
source: ReadSignal<S>,
|
||||
fetcher: impl Fn(&S) -> Fu + 'static,
|
||||
initial_value: Option<T>,
|
||||
) -> Resource<S, T, Fu>
|
||||
where
|
||||
S: Debug + Clone + 'static,
|
||||
T: Debug + Clone + 'static,
|
||||
Fu: Future<Output = T> + 'static,
|
||||
{
|
||||
let resolved = initial_value.is_some();
|
||||
let (value, set_value) = create_signal(cx, initial_value);
|
||||
let (loading, set_loading) = create_signal(cx, false);
|
||||
let (track, trigger) = create_signal(cx, 0);
|
||||
let fetcher = Rc::new(fetcher);
|
||||
let source = create_memo(cx, move |_| source());
|
||||
|
||||
// TODO hydration/streaming logic
|
||||
// TODO hydration/streaming logic
|
||||
|
||||
let r = Rc::new(ResourceState {
|
||||
scope: self,
|
||||
value,
|
||||
set_value,
|
||||
loading,
|
||||
set_loading,
|
||||
track,
|
||||
trigger,
|
||||
source,
|
||||
fetcher,
|
||||
resolved: Rc::new(Cell::new(resolved)),
|
||||
scheduled: Rc::new(Cell::new(false)),
|
||||
suspense_contexts: Default::default(),
|
||||
});
|
||||
let r = Rc::new(ResourceState {
|
||||
scope: cx,
|
||||
value,
|
||||
set_value,
|
||||
loading,
|
||||
set_loading,
|
||||
track,
|
||||
trigger,
|
||||
source,
|
||||
fetcher,
|
||||
resolved: Rc::new(Cell::new(resolved)),
|
||||
scheduled: Rc::new(Cell::new(false)),
|
||||
suspense_contexts: Default::default(),
|
||||
});
|
||||
|
||||
// initial load fires immediately
|
||||
self.create_effect({
|
||||
let r = Rc::clone(&r);
|
||||
move |_| r.load(false)
|
||||
});
|
||||
// initial load fires immediately
|
||||
create_effect(cx, {
|
||||
let r = Rc::clone(&r);
|
||||
move |_| r.load(false)
|
||||
});
|
||||
|
||||
let id = self.push_resource(r);
|
||||
let id = cx.push_resource(r);
|
||||
|
||||
Resource {
|
||||
runtime: self.runtime,
|
||||
scope: self.id,
|
||||
id,
|
||||
source_ty: PhantomData,
|
||||
out_ty: PhantomData,
|
||||
fut_ty: PhantomData,
|
||||
}
|
||||
Resource {
|
||||
runtime: cx.runtime,
|
||||
scope: cx.id,
|
||||
id,
|
||||
source_ty: PhantomData,
|
||||
out_ty: PhantomData,
|
||||
fut_ty: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +186,7 @@ where
|
|||
|
||||
let suspense_contexts = self.suspense_contexts.clone();
|
||||
let has_value = v.is_some();
|
||||
self.scope.create_effect(move |_| {
|
||||
create_effect(self.scope, move |_| {
|
||||
if let Some(s) = &suspense_cx {
|
||||
let mut contexts = suspense_contexts.borrow_mut();
|
||||
if !contexts.contains(s) {
|
||||
|
|
|
@ -2,30 +2,28 @@ use crate::{Runtime, Scope, ScopeId, Source, Subscriber};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::{any::Any, cell::RefCell, collections::HashSet, fmt::Debug, marker::PhantomData};
|
||||
|
||||
impl Scope {
|
||||
pub fn create_signal<T>(self, value: T) -> (ReadSignal<T>, WriteSignal<T>)
|
||||
where
|
||||
T: Clone + Debug,
|
||||
{
|
||||
let state = SignalState::new(value);
|
||||
let id = self.push_signal(state);
|
||||
pub fn create_signal<T>(cx: Scope, value: T) -> (ReadSignal<T>, WriteSignal<T>)
|
||||
where
|
||||
T: Clone + Debug,
|
||||
{
|
||||
let state = SignalState::new(value);
|
||||
let id = cx.push_signal(state);
|
||||
|
||||
let read = ReadSignal {
|
||||
runtime: self.runtime,
|
||||
scope: self.id,
|
||||
id,
|
||||
ty: PhantomData,
|
||||
};
|
||||
let read = ReadSignal {
|
||||
runtime: cx.runtime,
|
||||
scope: cx.id,
|
||||
id,
|
||||
ty: PhantomData,
|
||||
};
|
||||
|
||||
let write = WriteSignal {
|
||||
runtime: self.runtime,
|
||||
scope: self.id,
|
||||
id,
|
||||
ty: PhantomData,
|
||||
};
|
||||
let write = WriteSignal {
|
||||
runtime: cx.runtime,
|
||||
scope: cx.id,
|
||||
id,
|
||||
ty: PhantomData,
|
||||
};
|
||||
|
||||
(read, write)
|
||||
}
|
||||
(read, write)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{spawn::queue_microtask, ReadSignal, Scope, WriteSignal};
|
||||
use crate::{create_signal, spawn::queue_microtask, ReadSignal, Scope, WriteSignal};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct SuspenseContext {
|
||||
|
@ -24,7 +24,7 @@ impl Eq for SuspenseContext {}
|
|||
|
||||
impl SuspenseContext {
|
||||
pub fn new(cx: Scope) -> Self {
|
||||
let (pending_resources, set_pending_resources) = cx.create_signal(0);
|
||||
let (pending_resources, set_pending_resources) = create_signal(cx, 0);
|
||||
Self {
|
||||
pending_resources,
|
||||
set_pending_resources,
|
||||
|
|
|
@ -5,19 +5,17 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::{
|
||||
runtime::Runtime, spawn::queue_microtask, EffectId, MemoId, ReadSignal, Scope, ScopeId,
|
||||
SignalId, WriteSignal,
|
||||
create_effect, create_signal, runtime::Runtime, spawn::queue_microtask, EffectId, MemoId,
|
||||
ReadSignal, Scope, ScopeId, SignalId, WriteSignal,
|
||||
};
|
||||
|
||||
impl Scope {
|
||||
pub fn use_transition(self) -> Transition {
|
||||
let (pending, set_pending) = self.create_signal(false);
|
||||
Transition {
|
||||
runtime: self.runtime,
|
||||
scope: self,
|
||||
pending,
|
||||
set_pending,
|
||||
}
|
||||
pub fn use_transition(cx: Scope) -> Transition {
|
||||
let (pending, set_pending) = create_signal(cx, false);
|
||||
Transition {
|
||||
runtime: cx.runtime,
|
||||
scope: cx,
|
||||
pending,
|
||||
set_pending,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +57,7 @@ impl Transition {
|
|||
let set_pending = self.set_pending;
|
||||
// place this at end of task queue so it doesn't start at 0
|
||||
queue_microtask(move || {
|
||||
scope.create_effect(move |_| {
|
||||
create_effect(scope, move |_| {
|
||||
let pending = resources.borrow().iter().map(|p| p.get()).sum::<usize>();
|
||||
|
||||
if pending == 0 {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use leptos_reactive::create_scope;
|
||||
use leptos_reactive::{create_effect, create_memo, create_scope, create_signal};
|
||||
|
||||
#[test]
|
||||
fn effect_runs() {
|
||||
|
@ -6,12 +6,12 @@ fn effect_runs() {
|
|||
use std::rc::Rc;
|
||||
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(-1);
|
||||
let (a, set_a) = create_signal(cx, -1);
|
||||
|
||||
// simulate an arbitrary side effect
|
||||
let b = Rc::new(RefCell::new(String::new()));
|
||||
|
||||
cx.create_effect({
|
||||
create_effect(cx, {
|
||||
let b = b.clone();
|
||||
move |_| {
|
||||
let formatted = format!("Value is {}", a());
|
||||
|
@ -34,13 +34,13 @@ fn effect_tracks_memo() {
|
|||
use std::rc::Rc;
|
||||
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(-1);
|
||||
let b = cx.create_memo(move |_| format!("Value is {}", a()));
|
||||
let (a, set_a) = create_signal(cx, -1);
|
||||
let b = create_memo(cx, move |_| format!("Value is {}", a()));
|
||||
|
||||
// simulate an arbitrary side effect
|
||||
let c = Rc::new(RefCell::new(String::new()));
|
||||
|
||||
cx.create_effect({
|
||||
create_effect(cx, {
|
||||
let c = c.clone();
|
||||
move |_| {
|
||||
*c.borrow_mut() = b();
|
||||
|
@ -64,12 +64,12 @@ fn untrack_mutes_effect() {
|
|||
use std::rc::Rc;
|
||||
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(-1);
|
||||
let (a, set_a) = create_signal(cx, -1);
|
||||
|
||||
// simulate an arbitrary side effect
|
||||
let b = Rc::new(RefCell::new(String::new()));
|
||||
|
||||
cx.create_effect({
|
||||
create_effect(cx, {
|
||||
let b = b.clone();
|
||||
move |_| {
|
||||
let formatted = format!("Value is {}", cx.untrack(a));
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use leptos_reactive::create_scope;
|
||||
use leptos_reactive::{create_memo, create_scope, create_signal};
|
||||
|
||||
#[test]
|
||||
fn basic_memo() {
|
||||
create_scope(|cx| {
|
||||
let a = cx.create_memo(|_| 5);
|
||||
let a = create_memo(cx, |_| 5);
|
||||
assert_eq!(a(), 5);
|
||||
})
|
||||
.dispose()
|
||||
|
@ -12,9 +12,9 @@ fn basic_memo() {
|
|||
#[test]
|
||||
fn memo_with_computed_value() {
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(0);
|
||||
let (b, set_b) = cx.create_signal(0);
|
||||
let c = cx.create_memo(move |_| a() + b());
|
||||
let (a, set_a) = create_signal(cx, 0);
|
||||
let (b, set_b) = create_signal(cx, 0);
|
||||
let c = create_memo(cx, move |_| a() + b());
|
||||
assert_eq!(c(), 0);
|
||||
set_a(|a| *a = 5);
|
||||
assert_eq!(c(), 5);
|
||||
|
@ -27,11 +27,11 @@ fn memo_with_computed_value() {
|
|||
#[test]
|
||||
fn nested_memos() {
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(0);
|
||||
let (b, set_b) = cx.create_signal(0);
|
||||
let c = cx.create_memo(move |_| a() + b());
|
||||
let d = cx.create_memo(move |_| c() * 2);
|
||||
let e = cx.create_memo(move |_| d() + 1);
|
||||
let (a, set_a) = create_signal(cx, 0);
|
||||
let (b, set_b) = create_signal(cx, 0);
|
||||
let c = create_memo(cx, move |_| a() + b());
|
||||
let d = create_memo(cx, move |_| c() * 2);
|
||||
let e = create_memo(cx, move |_| d() + 1);
|
||||
assert_eq!(d(), 0);
|
||||
set_a(|a| *a = 5);
|
||||
assert_eq!(c(), 5);
|
||||
|
@ -51,14 +51,14 @@ fn memo_runs_only_when_inputs_change() {
|
|||
|
||||
create_scope(|cx| {
|
||||
let call_count = Rc::new(Cell::new(0));
|
||||
let (a, set_a) = cx.create_signal(0);
|
||||
let (b, _) = cx.create_signal(0);
|
||||
let (c, _) = cx.create_signal(0);
|
||||
let (a, set_a) = create_signal(cx, 0);
|
||||
let (b, _) = create_signal(cx, 0);
|
||||
let (c, _) = create_signal(cx, 0);
|
||||
|
||||
// pretend that this is some kind of expensive computation and we need to access its its value often
|
||||
// we could do this with a derived signal, but that would re-run the computation
|
||||
// memos should only run when their inputs actually change: this is the only point
|
||||
let c = cx.create_memo({
|
||||
let c = create_memo(cx, {
|
||||
let call_count = call_count.clone();
|
||||
move |_| {
|
||||
call_count.set(call_count.get() + 1);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use leptos_reactive::create_scope;
|
||||
use leptos_reactive::{create_scope, create_signal};
|
||||
|
||||
#[test]
|
||||
fn basic_signal() {
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(0);
|
||||
let (a, set_a) = create_signal(cx, 0);
|
||||
assert_eq!(a(), 0);
|
||||
set_a(|a| *a = 5);
|
||||
assert_eq!(a(), 5);
|
||||
|
@ -14,8 +14,8 @@ fn basic_signal() {
|
|||
#[test]
|
||||
fn derived_signals() {
|
||||
create_scope(|cx| {
|
||||
let (a, set_a) = cx.create_signal(0);
|
||||
let (b, set_b) = cx.create_signal(0);
|
||||
let (a, set_a) = create_signal(cx, 0);
|
||||
let (b, set_b) = create_signal(cx, 0);
|
||||
let c = move || a() + b();
|
||||
assert_eq!(c(), 0);
|
||||
set_a(|a| *a = 5);
|
||||
|
|
Loading…
Reference in a new issue