Mielipuoli
Mielipuoli

Reputation: 1510

How to update an array after splice in Svelte?

I'm learning Svelte, and read in the documentation that arrays need to be reassigned in order for a component or page to update it. For that they devised a more idiomatic solution. Instead of writing:

messages.push('hello');
messages = messages;

you can write instead:

messages = [...messages, 'hello'];

Alright, makes sense. But then the documentation says:

You can use similar patterns to replace pop, shift, unshift and splice.

But how? I cannot see how you can remove items from an array. More to the point, how could I write the following more idiomatically?

messages.splice(messages.indexOf('hello'), 1);
messages = messages;

Upvotes: 37

Views: 18098

Answers (7)

ZER0
ZER0

Reputation: 25322

There are several things to consider here. Given this code:

messages.splice(messages.indexOf('hello'), 1);
messages = messages;

What's happening here is:

  1. Looking for the first occurrence of the string "hello" in the array
  2. Removing such element from the array, based on the index found.

The assumption here is that "hello" needs to exists, otherwise the could would remove the last item from the array (since indexOf returns -1).

The original array is therefore mutate: depends by the context, that sometimes can be preferable instead of copying the whole array into a new one; otherwise it's generally a better practice avoid such mutation.

So. If you want to have this behavior exactly, probably this is the best code you can have. For example, takes the filter example:

messages = messages.filter(message => message !== "hello")

What's happening here is:

  1. Filter out any element equals to "hello"
  2. Returns a new array without such element

So it's quite different from the original code: first of all, it always loop the whole array. If you have thousands of element, even if you have only one "hello" at the second index, it would always iterate all of them. Maybe it's what you want, maybe not. If the element is unique, such as an id, maybe you want to stop once you find it. Second, it returns a new array. Again, that usually a better practice than mutate the array, but in some context it's preferable mutate it instead of create a new one.

So, if you want to mutate the original array, it's probably better to stick to your original code.

If, instead, you don't care (such as the example of push), I believe that in the intention of svelte's developers, your code would be roughly translate to:

let i = messages.indexOf("hello"); 
messages = [...messages.slice(0, i), ...messages.slice(i + 1)];

(Still assuming there is a "hello" message and you're interested only in the first occurrence).

It's unfortunate that JS doesn't have a better syntax to handles slices.

Upvotes: 8

Michael McGinnis
Michael McGinnis

Reputation: 1019

As mentioned, Svelte's reactivity is triggered by assignments. The current Svelte tutorial uses JavaScript's (ES6) spread syntax (three dots) to add the next-higher number to an array, providing a more idiomatic solution than a redundant assignment using push:

function pushNumber() {     
    numbers = [...numbers, lastnumber]; // 1, 2, 3, 4, 5
}

You could use spread syntax to replace popshiftunshift and splicethough it might increase the time and complexity of the operation in some cases:

function unshiftNumber() {  
    numbers = [firstnumber, ...numbers]; // 0, 1, 2, 3, 4
}

function popNumber() {
    numbers = [...numbers.slice(0,numbers.length - 1)]; // 1, 2, 3
}

function shiftNumber() {
    numbers = [...numbers.slice(1,numbers.length)]; // 2, 3, 4
}

function spliceNumber() {
    numbers = [firstnumber, ...numbers.slice(0,numbers.length-1)]; // 0, 1, 2, 3
}   

Spread is just one way to do it, though. The purpose behind not using pop/push etc is to encourage immutability. So any removal can just be a filter, for example.

Upvotes: 21

fish
fish

Reputation: 1

You can try this: https://svelte.dev/repl/0dedb37665014ba99e05415a6107bc21?version=3.53.1

use a library called svelox. It allows you to use the Array native api(push/splice...etc.) without reassignment statements.

Upvotes: 0

Gabriel F. Engel
Gabriel F. Engel

Reputation: 980

In case you're wandering, filter can also be used to remove elements using a given index:

let elements = ['a','b', 'c'];
let idx = 1;
elements = elements.filter( (e,i) => i !== idx );
// => ['a', 'c']

Upvotes: 4

Parables Boltnoel
Parables Boltnoel

Reputation: 11

You can perform the usual push and pop or `splice on your Array

But because Svelte's reactivity is triggered by assignments, using array methods like push and splice won't automatically cause updates.

According to All about Immutable Arrays and Objects in JavaScript you can do it this way...

let messages = ['something', 'another', 'hello', 'word', 'another', 'again'];

const indexOfHello = messages.indexOf('hello');

messages = [...messages.slice(0, indexOfHello), ...messages.slice(indexOfHello + 1)];

Note the difference between splice and slice

The splice() method adds/removes items to/from an array, and returns the removed item(s). Note: This method changes the original array. Syntax: array.splice(start, deleteCount, itemstoAdd, addThisToo);

But

The slice() method returns the selected elements in an array, as a new array object. The slice() method selects the elements starting at the given start argument, and ends at, but does not include, the given end argument. Note: The original array will not be changed.

In order words

It return a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified. Syntax: array.slice(start, end);

Upvotes: 1

Webcoder31
Webcoder31

Reputation: 75

Spread the spliced array to reassign it to itself ;)

messages = [...messages.splice(messages.indexOf('hello'), 1)];

The goal is to make Svelte detecting that array messages (a property of your component or a variable in the Svelte store) has changed. This is why the array messages must be declared with let or var keyword, not const. This way you're allowed to reassign it. And the reassign operation itself is sufficient to make Svelte detecting that the array has changed.

Perhaps even, simply by doing so works too:

messages = messages.splice(messages.indexOf('hello'), 1);

Upvotes: -2

Tholle
Tholle

Reputation: 112777

You could e.g. use the filter array method to create a new array without the element 'hello':

messages = messages.filter(m => m !== 'hello');

Upvotes: 34

Related Questions