Svelte: Reassigning $$props to another variable and using the spread operator casues issues with input's

Created on 9 Jul 2020  路  5Comments  路  Source: sveltejs/svelte

Describe the bug
If you reassign $$props to another variable and use it with the spread operator on two or more inputs, typing into one clears the value from the other.
Only the displayed value is cleared, any bound variables retain the correct value.

To Reproduce
https://svelte.dev/repl/59246207fcc949b1b5691b045d3bbd7e?version=3.24.0

Type something in each input box and observe the behaviour for the top two.
Typing in one clears the other.

Expected behavior
Previously to v3.24.0, it was possible to use $$props this way without any issue.

Information about your Svelte project:
Firefox 79b, Arch Linux, Svelte 3.24.0, rollup

Severity
This isn't a blocker as such because, depending on the specific case, a workaround ought to be easy to find.
However it's exhibited in a dependency of my project in which I can't readily implement a work around.
Namely sveltestrap (issue https://github.com/bestguy/sveltestrap/issues/157).

props bug has pr

All 5 comments

Yes, it is a bug.

And also, using const props = $$props, the props will always remain as the initial $$props of the component. it will not be reactive, unless

$: props = $$props;

Secondly, $$props will contain value, which you already bind it again. if you want to spread remaining props that is not declared, you can use $$restProps.

It seems like this happens because you effectively assign the value of the value attribute of the input element twice: The first time because value is passed as a prop due to the binding in the parent component and the second time value is assigned as a result of the bind:value of the input element. That means that your example is similar to the following:

<script>
    export let value;
    let something = {value: "12"};
</script>
<button on:click={() => something.value++}>Increment</button>
<input {...something} type="text" bind:value />

In this example the value of the input element is also assigned twice: Because it is part of the spread {...something} and also because a binding is specified. The Component2 works because the two assignments always match up because the $$props is reactive and will update automatically, but const props = $$props will not update and will thus always be {value: ""}. When the parent component updates Component1 will trigger a spread update and reassign value resulting in the value being set to "" again. Currently the generated code calls component0.$set(component0_changes); also if component0_changes is empty.

We could potentially add an additional check around https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts#L518 so that $set is only called if there is at least one change in ${name_changes}.

@tanhauhau What's the exact semantic if an attribute is assigned twice? Is that undefined behavior or is it supposed to be the value that was assigned last? Could we maybe add a warning if a spread contains a key that is already set on that element?

We could potentially add an additional check around https://github.com/sveltejs/svelte/blob/master/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts#L518 so that $set is only called if there is at least one change in ${name_changes}

yes, there's already an similar existing issue https://github.com/sveltejs/svelte/issues/4993

What's the exact semantic if an attribute is assigned twice? Is that undefined behavior or is it supposed to be the value that was assigned last?

I am not sure whether it is documented anywhere, so I dont have an official answer for this. but imo, the last assigned value should prevail.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bestguy picture bestguy  路  3Comments

Rich-Harris picture Rich-Harris  路  3Comments

st-schneider picture st-schneider  路  3Comments

Rich-Harris picture Rich-Harris  路  3Comments

lnryan picture lnryan  路  3Comments