Feature request: computed
rcoopr opened this issue · comments
I like the DX of using the signal.value
by default and accessing the underlying signal with the $
prefix, but context switching when using computed values is awkward. It would be nice to provide computed
with the same functionality as Preact's computed
, only returning a proxy object instead for the improved DX
I'm not sure what you mean. Could you please explain it again using an example?
Currently, we have
import { computed, effect } from '@preact/signals-core';
import { deepSignal } from 'deepsignal/core';
const deep = deepSignal({
a: 5,
b: 10
})
const multiplied = computed(() => ({
a: deep.a * 2,
b: deep.b * 2,
}))
effect(() => {
// some effect using a combination of deepSignals directly, and computed values
console.log(deep.a, multiplied.value.a)
// ^ above, we can use `deep.a` but not `multiplied.a`
})
// proposed:
effect(() => {
console.log(deep.a, multiplied.a)
// accessing `multiplied.a` would return the value
// after all, `computed`s are also signals
})
Some care might need to be given to the case where a computed
returns a primitive, so the current way of accessing the signal through obj.$prop
wouldn't work the same
i.e:
const primitive = computed(() => deep.a * 2)
// `primitive` would return a value, but where is the signal? `primitive.$`?
Is there something wrong with getters? They use computed
underneath and they'll give you the same $
API as regular signals:
import { effect } from "@preact/signals-core";
import { deepSignal } from "deepsignal/core";
const deep = deepSignal({
a: 5,
b: 10,
});
const multiplied = deepSignal({
get a() {
return deep.a * 2;
},
get b() {
return deep.b * 2;
},
});
effect(() => {
// this works fine now
console.log(deep.a, multiplied.a);
});
You can also use them in the same object:
import { effect } from "@preact/signals-core";
import { deepSignal } from "deepsignal/core";
const deep = deepSignal({
a: 5,
b: 10,
get doubleA() {
return deep.a * 2;
},
get doubleB() {
return deep.b * 2;
},
});
Sorry, I glossed over that in the docs. I suppose the case where a computed
returns a primitive is sort of outside the scope for this module dealing with objects
I suppose the case where a computed returns a primitive is sort of outside the scope for this module dealing with objects
Do you mean this?
const primitive = computed(() => deep.a * 2)
// `primitive` would return a value, but where is the signal? `primitive.$`?
Yeah
Then, yeah. If you just have a primitive value, you can't know when it's being written or read. The only exception is Svelte components because they are compiled.
// Solid read
count();
// Solid write
setCount("hi");
// Preact / Vue read
count.value;
// Preact / Vue write
count.value = "hi";
// DeepSignal read
state.count;
// DeepSignal write
state.count = "hi";
// Svelte read
count;
// Svelte write
count = "hi";
// Svelte (final code after compilation)
$$invalidate(0, count = "hi");
Closing now but feel free to reopen if there's anything else that you want to discuss.