Skip to content
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

Svelte 5: Add a way to mark objects as stale #11423

Closed
TheCymaera opened this issue May 2, 2024 · 9 comments
Closed

Svelte 5: Add a way to mark objects as stale #11423

TheCymaera opened this issue May 2, 2024 · 9 comments

Comments

@TheCymaera
Copy link
Contributor

TheCymaera commented May 2, 2024

Describe the problem

In previous versions of Svelte, reassigning objects to a store or variable will trigger an update.
For example, this will log to the console every second.

<script lang="ts">
let obj = {};
setInterval(()=>obj = obj, 1000)

$: {
	obj;
	console.log("An update happened")
}
</script>

In Svelte 5, states do not cause an update during object reassignment, due to the new fine grained reactivity model.
This will only log once during initialization.

<script lang="ts">
let obj = $state({});

setInterval(()=>obj = obj, 1000)

$effect(()=>{
	obj;
	console.log("An update happened")
})
</script>

This is an issue, I often use re-assignment to mark data from external APIs as stale.
This has prevented me from replacing some stores with state-runes, as the only workaround is to clone the object, adding overhead.

externalAPI.onChange(()=>myStore.set(externalAPI.value))

Describe the proposed solution

Add a rune to mark some data as stale, forcing it and all of its dependencies to update.

externalAPI.onChange(()=>$markStale(myState))

Importance

would make my life easier

@TheCymaera TheCymaera changed the title Svelte 5: Add a way to mark external data as stale Svelte 5: Add a way to mark objects as stale May 2, 2024
@luisfontes
Copy link

Isn't this what $state.frozen is for?

@TheCymaera
Copy link
Contributor Author

TheCymaera commented May 2, 2024

$state.frozen doesn't seem to trigger an update either. In fact, it freezes the object, preventing the external API from mutating it.
https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAEyWOwYqDQBBEf2VocnBAsu7VjYLHfMN2DqP2ZJVJ9zDTClnx34PJsV69gtrAT4Ey1L8bsHsQ1NDFCCXoMx4hrxSUoIQsSxoOcslDmqKa4PjeIGhGaJEDqZF-No05ZXVKZ5_kn7jYdvuDjJxJr6yUVheKwjbtx5V-Ls13VVX2cE7kPQ367jdkVOnnY6yDcJZA5yD3AqFjs8TRKZk_FyMxjQgWebfIl6_PuRZKeMg4-YlGqDUttN_2F9f91XzrAAAA

@brunnerh
Copy link
Member

brunnerh commented May 2, 2024

You can nest the data in a simple wrapper, then create a new wrapper in the assignment.
The actual data itself will not need to be copied.

let obj = $state({ data: {} });
setInterval(() => obj = { ...obj }, 1000)

@paoloricciuti
Copy link
Contributor

Try to use $effect sparsely: it should be used solely to sync a reactive variable with some imperative API. Abusing it to trigger a re fetch doesn't seem like a good idea where you could just write a function that fetch and assign and call that in the code instead of reassigning.

@7nik
Copy link

7nik commented May 2, 2024

What are your real cases? Why do you want to do something if the data didn't change?

@trueadm
Copy link
Contributor

trueadm commented May 2, 2024

You can use a class to handle external state and trigger invalidate on it to do as you want:

class External {
    #data;
    #version = $state(0);

    constructor(data) {
        this.#data = data;
    }
    get data() {
        this.#version;
        return this.#data;
    }
    set data(_data) {
        this.#version++;
        this.#data = _data;
    }
    invalidate() {
        this.#version++;
    }
}

@mjadobson
Copy link

A quick and dirty fix to get the v4 behaviour (without object cloning) is to set the variable to undefined, and then reset it back to the previous state:

<script lang="ts">
	let obj = $state({});
	
	setInterval(()=> {
		let tmp = obj;
		obj = undefined;
		obj = tmp;
	}, 1000)
	
	$effect(()=>{
		obj;
		console.log("An update happened")
	})
</script>

https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE02PwWrDMBBEf0UsOVhgWvfqxIYc8w3dHFRrlarIK2GtA0Ho34uSHnqc4b2BKeB8oAzjZwE2K8EI55SgB3mkFvKdghD0kOO-La055WXzSVQwfJsQJCPMyIFExa8fNalDFiPUlaqPyMiZ5MJC292ErtPTrAoySsNlTWpqUuPkJe9syXkm-6-TNR2Ra68-hmHQbfNAztEiz73yBz6FJXKOgd5CvHUIZ1Z7skZIfZuUiMkiaOSqkU_vrxcz9LBG650nC6NsO9Vr_QUdi1XSFAEAAA==

@TheCymaera
Copy link
Contributor Author

TheCymaera commented May 6, 2024

I just had a look at the reactive collections provided by Svelte (in svelte/reactivity). It works by using an incrementing counter similar to what trueadm suggested.

I'm glad to find some workarounds, but it would be nice to have a more idiomatic solution down the line. I'm willing to contribute a PR if the members can agree on a solution.

@trueadm
Copy link
Contributor

trueadm commented May 11, 2024

This isn't something we feel needs to be a rune or in the core of Svelte, given how you can address this in various different ways in userland. So closing this for now.

@trueadm trueadm closed this as completed May 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants