fix: keep fallback value after spread update not setting that prop (#9717)

fixes #9716
pull/9721/head
Simon H 1 year ago committed by GitHub
parent 65fa717ccd
commit 4a8f0bc7e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: keep fallback value after spread update not setting that prop

@ -1430,6 +1430,7 @@ export function prop_source(props_obj, key, default_value, call_default_value) {
.i;
let ignore_next1 = false;
let ignore_next2 = false;
let did_update_to_defined = !should_set_default_value;
let mount = true;
sync_effect(() => {
@ -1445,8 +1446,13 @@ export function prop_source(props_obj, key, default_value, call_default_value) {
return;
}
if (not_equal(immutable, propagating_value, source_signal.v)) {
if (
// Ensure that updates from undefined to undefined are ignored
(did_update_to_defined || propagating_value !== undefined) &&
not_equal(immutable, propagating_value, source_signal.v)
) {
ignore_next2 = true;
did_update_to_defined = true;
// TODO figure out why we need it this way and the explain in a comment;
// some tests fail is we just do set_signal_value(source_signal, propagating_value)
untrack(() => set_signal_value(source_signal, propagating_value));
@ -1469,6 +1475,7 @@ export function prop_source(props_obj, key, default_value, call_default_value) {
if (not_equal(immutable, propagating_value, possible_signal.v)) {
ignore_next1 = true;
did_update_to_defined = true;
untrack(() => update_bound_prop(propagating_value));
}
});

@ -0,0 +1,5 @@
<script>
const { propA, propB = "fallback" } = $props();
</script>
<p>{propA} {propB}</p>

@ -0,0 +1,55 @@
import { test } from '../../test';
// Tests that fallback values are kept as long as the prop is not defined in the case of a spread
export default test({
accessors: false, // so that propA actually becomes $.prop and not $.prop_source
html: `
<button>change propA</button>
<button>change propB</button>
<p>true fallback</p>
`,
async test({ assert, target }) {
const [propA, propB] = target.querySelectorAll('button');
await propA.click();
assert.htmlEqual(
target.innerHTML,
`
<button>change propA</button>
<button>change propB</button>
<p>false fallback</p>
`
);
await propB.click();
assert.htmlEqual(
target.innerHTML,
`
<button>change propA</button>
<button>change propB</button>
<p>false defined</p>
`
);
await propA.click();
assert.htmlEqual(
target.innerHTML,
`
<button>change propA</button>
<button>change propB</button>
<p>true defined</p>
`
);
await propB.click();
assert.htmlEqual(
target.innerHTML,
`
<button>change propA</button>
<button>change propB</button>
<p>true</p>
`
);
}
});

@ -0,0 +1,12 @@
<script>
import Component from './Component.svelte';
let props = $state({
propA: true,
propB: undefined
});
</script>
<button on:click={() => {props.propA = !props.propA}}>change propA</button>
<button on:click={() => {props.propB = props.propB ? undefined : 'defined'}}>change propB</button>
<Component {...props}/>
Loading…
Cancel
Save