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
feat: generic utility for making any class reactive #11504
base: main
Are you sure you want to change the base?
Conversation
|
This is still in draft mode because in this code: <script>
import {Set} from "svelte/reactivity";
const data = $state([1,2,3]);
const reactiveSet = new Set(data);
$effect(() => {
console.log("has 1", reactiveSet.has(1));
});
$effect(() => {
console.log("has 2", reactiveSet.has(2));
});
$effect(() => {
console.log("has 3", reactiveSet.has(3));
});
$effect(() => {
console.log("has 5", reactiveSet.has(5));
});
</script>
<button onclick={()=>{
reactiveSet.delete(2);
}}>
delete 2
</button>
<button onclick={()=>{
reactiveSet.add(2);
}}>
add 2
</button>
<button onclick={()=>{
reactiveSet.clear();
}}>
clear
</button>
when we delete <script>
import {Set} from "svelte/reactivity";
const data = $state([1,2]);
const reactiveSet = new Set(data);
$effect(() => {
console.log("has 1", reactiveSet.has(1));
});
$effect(() => {
console.log("has 2", reactiveSet.has(2));
});
</script>
<button onclick={()=>{
reactiveSet.delete(2);
}}>
delete 2
</button> after clicking on "delete 2" only |
As you saw, the general problem with this approach is that the object isn't fine-grained. You want $effect(() => {
console.log(reactiveMap.get(42));
}); run only when a value with the key 42 is added or set but not any other. There are also some debates about whether the stored item should be reactified. |
I feel like this is quite nice generic solution for making arbitrary things reactive easily (e.g. objects from 3rd party libraries that you have no control over). However as @7nik pointed out, this isn't fine-grained. So, I think that basic things like Set/Map should be implemented manually, to make them as fine-grained as possible. |
@7nik @Azarattum, I completely agree with you. I've marked this as a draft while I try to comeup with a solution (hopefully) that addresses this issue. However, as noted in issue #11515, the current implementation still faces a similar challenge from a different angle. For example, if a <script>
import {Set} from "svelte/reactivity";
const data = [1];
const reactiveSet = new Set(data);
$effect(() => {
console.log("has 1", reactiveSet.has(1));
});
$effect(() => {
console.log("has 2", reactiveSet.has(2));
console.log("has 3", reactiveSet.has(3));
// and so one for another 100000 elements that dont exist in the set and might never do!
});
</script> basically each |
Well, to react to a 1000 items changing finely, we would need a 1000 signals. I think in this case this is what the user has requested explicitly, so it feels like an expected behavior. I think having a lot of signals that trigger less is better than a few that trigger often. This is what the fine-grainness is all about. Though I think that it would still be more common to have less signals than items in a set. So, lazy signal initialization shouldn't be an issue. |
I thought about storing in Map super items of shape Regarding reactivity for non-existing keys, just creating and storing signals for them can cause a memory leak when somebody uses huge objects as keys or adds and removes thousands of unique keys. Temporary returning derived (kind of |
@7nik wouldn't this be a good candidate for a WeakMap for storing the signals, to not hold strongly to the user's potentially huge Map key or Set value? |
fixed some issues thanks to the comments above, also fixes #11515 |
…erty doesnt *require* one
2- removed templated constructor params cause it didn't do anything (types)
…d (because we already got it after delete)
…gnal and doesn't require its own signal
ce43a21
to
31800ee
Compare
31800ee
to
1457398
Compare
I came up with better idea to make it more accurate. Because currently it makes signals aware that a change "is going to happen", the correct implementation would be "a change has happened". |
This is a completly new take on the reactivity package. After issues like #11222 and solutions like #11200, #11385 and #11287 we still have data duplication and a complex solution (also some other issues which makes current implementation not fine grained like #11515). This implementation allows us to make any (hopefully) builtins reactive without any code repetition in more flexible and generic way. This we only care about notifying the read methods about changes and not the data management itself. This pr also uses the #11287 idea to only create signals when they are actually read, not on initialization which could be a performance buff - for instance in my tests with a map which has 1_000_000 elements, the page load speed increased by 1s.
if the idea is accpeted and actually good I'll refactore other reactivity package classes to use this utility instead.
using this utility on
Set
for instance resulted in:Svelte 5 rewrite
Please note that the Svelte codebase is currently being rewritten for Svelte 5. Changes should target Svelte 5, which lives on the default branch (
main
).If your PR concerns Svelte 4 (including updates to svelte.dev.docs), please ensure the base branch is
svelte-4
and notmain
.Before submitting the PR, please make sure you do the following
feat:
,fix:
,chore:
, ordocs:
.Tests and linting
pnpm test
and lint the project withpnpm lint