svelte: deleting object properties doesn't trigger a render

Maybe this has been discussed at some point, maybe not, but I was surprised that

obj.b = 'foo';

…will trigger a re-render, but…

delete obj.b;

…won’t.

REPL: https://svelte.dev/repl/c5377d8c85e34735a792a29e1b2d1353?version=3.6.6

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 22 (12 by maintainers)

Most upvoted comments

I think the simplest approach is = sign will trigger an update.

After completing the tutorial a few weeks ago, the rules in the back of my head were:

  1. Fiddling with arrays or objects using methods won’t cause re-renders
  2. Fiddling with arrays or objects not using methods will cause re-renders

To me it’s more logical to group the different behaviors based on the fact that 1) are method calls, 2) are not.

// Won't re-render
arr.push(1);                              // as expected
arr.unshift(3);                           // as expected

// Will re-render
arr[3] = 4;                               // as expected

// Won't re-render
Object.assign(obj, { bar: 'foo' });       // as expected

// Will re-render
obj.foo = 'bar';                          // as expected
delete obj.foo;                           // Surprise!

I’m not going to die on this hill, but just wanted to point this out as being the more logical approach in my opinion.

Since delete participants[name] will return true, what about participants = delete participants[name] && participants

Another vote for triggering reactive update with delete. Unless there’s a better alternative to this(?):

  function handleKeyUp(event) {
    delete $activeKeys[event.key]
    $activeKeys = $activeKeys; // https://github.com/sveltejs/svelte/issues/3211#issuecomment-599840588
  }

I can’t tell if you’re joking @pngwn but if I offended I can see how my comment could have been offensive and I’m sorry. It was intended to be commentary on javascript, not your code in particular.

@Conduitry I rather agree with @arggh and I believe delete should be implemented. Here is an extra argument:

For a push we have an alternative: array = [ ...array, {} ] But for delete we don’t so we need to add a bit of silliness to the end and there is no efficient workaround:

delete object[prop]
object = object // to force refresh of the property

The rule could simply be everything but method calls work. Which is also a rather simple rule, but more convinient.

I don’t remember this coming up, but I’d say this is expected. Only stuff that’s explicitly an assignment will trigger invalidation and re-rendering. There are already a bunch of things (like calling .push() on an array for example) that cause data to be updated that we can’t in general catch. So, while we could easily detect stuff like delete, I’d argue that it’s better to have a dead simple rule for what works (‘has to be an assignment’) than it is to work in more cases.

Since delete participants[name] will return true, what about participants = delete participants[name] && participants

This retains the performance characteristics of an in-place delete, so thank you for mentioning this.

It’s an extra 50 bytes in your bundle, but worth it for readability, I think 😉

It is 3 times that gzipped. Although probably smaller in the context of a bundle.

If utilities are on the table then I raise you a function so small that gzipping it will make it bigger. This could be smaller in the context of a bundle too.

https://svelte.dev/repl/7d9aa252be864a1aaf5ab02f6e658ce6?version=3.24.1

This is a small, fun library if you like functional utilities: https://github.com/ascartabelli/lamb

const userLeft = (participantId) => {
  participants = Object.entries(participants).reduce((acc, [k, v]) => {
    return k !== participantId ? (acc[k] = v, acc) : acc
  }, {})
}

As a “using Svelte for 3 months” user, this tripped me up as well. I can’t think of a reasonable way to do what @benwoodward mentioned with deleting an object’s key, without the use of a useless-looking assignment thereafter. In my case the code is very similar:

  const userLeft = (participantId) => {
    delete participants[participantId]
    participants = participants
  }