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:
Greg Johnston 2022-08-16 09:56:00 -04:00
parent 329eab89d2
commit 02558c2f66
9 changed files with 147 additions and 155 deletions

View file

@ -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,

View file

@ -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)]

View file

@ -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) {

View file

@ -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)]

View file

@ -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,

View file

@ -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 {

View file

@ -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));

View file

@ -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);

View file

@ -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);