-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
feat: allow $derived($state()) for deep reactive deriveds
#17308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: e846ebb The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
|
YES. let local_arr = $derived.by(()=>{
let _ = $state($state.snapshot(arr));
return _;
});Which translates to let local_arr = $derived($state($state.snapshot(arr)));It's up to debate whether this should be the default behavior |
|
Alternatively, we could expose a This would also have the advantage of working in a universal |
I have been doing this for months in userland by exporting functions that wrap |
|
This would be most helpful. After the recent change to warn In terms of naming/conventions, I've also seen the suggestion in some other issues for <script>
const { client } = $props()
let cats = $state.from(() => {
client.cats.map((cat) => ({
...cat,
_id: Math.random(),
})),
})
function addCat() {
cats.push({
_id: Math.random(),
})
}
</script>
{#each cats as cat (cat._id)}
<input type="text" value="{cat.name}" /><br />
{/each}
<button onclick={addCat} title="Add Cat">Add Cat</button>In the above, if the client prop changed, any $state.from would get recalculated |
|
Pretty sure any version where $state is the leading text is wrong. $state denotes a source. Which this is not, this derives it's source. |
|
My preference would be But I'm also fine with |
|
One question with this approach would also be reassignments. let thing = $derived($state(array));
thing = otherArray;Is So having a thing = $state(otherArray); // not allowed
thing = proxify(otherArray); // OK |
|
The function proxify(value) {
const proxified = $state(value);
return proxified;
}
let foo = $derived(proxify(...));src: #16189 (comment) |
If you reassign to the derived, it will break reactivity, yes. If you reassign to the prop/state it's based on it will work correctly. https://svelte.dev/playground/1a0230ade38a4ddd8ebc64ecfa8c5d95?version=5.45.6 This foot gun would exist regardless of whether you use |
|
To avoid reassignment: function box<T>(value: T): { current: T } {
const boxed = $state({
current: $state.snapshot(value)
});
return boxed;
}
const thing = $derived(box(array));
thing.current = otherArray; |
The difference is that in one case you can avoid the reactivity loss using the same syntax ( And as noted, if the wrapping happens inside a dedicated rune, the problem would not exist at all. |
|
I think it's possible to make |
That still results in reactivity loss. https://svelte.dev/playground/663f9bf9fe4e420c8b398b6b83256d16?version=5.45.6
That does more intuitively help you avoid the foot gun. Though the foot gun still exists if you reassign to thing. However, it's at least mitigated because you're far more likely to reassign to If not making a new rune, this is the method I'd probably go with. Otherwise, as stated, you'd probably need another warning. |
|
Sorry if this question makes you smile (I'm the least appropriate person to answer these technical questions): why can't we have a deep reactive |
|
My assumption is that |
|
For minimal magic-guts-exposing (exposing the behavior without lifting the nuts-and-bolts of how reactivity proxies work right to the user's face), maybe some extensions on derived would serve well? const thing = $derived.proxy(array);
const thing = $derived.proxyOf(() => {
// if possible
return array
});That would satisfy:
Edit: not as a replacement for what this does, but an extension. |
This is the most compelling argument I've seen for a new rune. |
|
I'm not sure Some options being tossed around + some new ones:
My personal preference is for options 1 or 3, but given how often I need this in our codebase, I'd be happy with any of them :-) Another alternative would be to allow a way to make $derived deeply reactive (substitute
|
This needs discussion, but Dominic and I had this idea. Deriveds are not deeply reactive, but even nowadays you can kinda achieve deep reactivity for deriveds by doing
This is fine, but it's a bit boilerplatey. This PR is to allow a shorthand of this by allowing
$stateto be used in theinitof a derived.This basically gets compiled to
So it works exactly the same.
Point of discussions:
$stateand not$state.rawyou are actually mutating the parentHowever, this is also true for the original trick that we currently "kinda" recommend.
I look at the server output, and it doesn't need any changes, since
$stateis just removed anyway.WDYT should we do this?
Before submitting the PR, please make sure you do the following
feat:,fix:,chore:, ordocs:.packages/svelte/src, add a changeset (npx changeset).Tests and linting
pnpm testand lint the project withpnpm lint