Reputation: 1510
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
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:
"hello"
in the arrayThe 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:
"hello"
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
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 pop
, shift
, unshift
and splice
though 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
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
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
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
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