I have a simple object in state, string -> objects. My UI renders the keys and values in a loop. Each object has a delete button. On click, I want to delete keys (or set them to undefined) such that the UI updates.
This page in the Svelte tutorial says:
A simple rule of thumb: the name of the updated variable must appear on the left hand side of the assignment. For example this...
const obj = { foo: { bar: 1 } }; const foo = obj.foo; foo.bar = 2;
...won't trigger reactivity on obj.foo.bar, unless you follow it up with obj = obj.
Sure enough, if I handle the click like:
{#each Object.entries(objInState) as [key, value]}
{value}
<button on:click={_ => delete objInState[key]}
{/each}
... the page misbehaves and doesn't immediately update. (Same with setting to undefined.)
And sure enough, when I change that to:
{#each Object.entries(objInState) as [key, value]}
{value}
<button on:click={_ => {
delete objInState[key]
objInState = objInState
}}
{/each}
... it works.
But it's so ugly and confusing to read. Is there a way to achieve this behavior without assigning a variable to itself?
There is a pattern of constructing a new object which necessitates an assignment and thus will always trigger reactivity (in Svelte 3/4); this is common for arrays:
array = array.filter(v => v != valueToDelete)
You can do something similar here, but it is significantly less pretty for objects:
object = Object.fromEntries(
Object.entries(object).filter(([k]) => k != key)
)
(Though, the logic could be extracted to a function.)
In Svelte 5 delete
alone already works with $state
.