mirror of https://github.com/sveltejs/svelte
fix: on teardown, use the last known value for the signal before the set (#15469)
* fix: on teardown, use the last known value for the signal before the se * fix: on teardown, use the last known value for the signal before the se * fix: on teardown, use the last known value for the signal before the se * fix: on teardown, use the last known value for the signal before the se * fix: on teardown, use the last known value for the signal before the se * Update packages/svelte/src/internal/client/reactivity/props.js Co-authored-by: Rich Harris <rich.harris@vercel.com> * Update packages/svelte/src/internal/client/reactivity/props.js Co-authored-by: Rich Harris <rich.harris@vercel.com> * Update packages/svelte/src/internal/client/reactivity/props.js Co-authored-by: Rich Harris <rich.harris@vercel.com> * lint * lint * lint * Update .changeset/sharp-elephants-invite.md --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>pull/15493/head
parent
1cc5bcdc99
commit
110d42062f
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': minor
|
||||
---
|
||||
|
||||
fix: make values consistent between effects and their cleanup functions
|
@ -0,0 +1,11 @@
|
||||
<script>
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
export let my_prop;
|
||||
|
||||
onDestroy(() => {
|
||||
console.log(my_prop.foo);
|
||||
});
|
||||
</script>
|
||||
|
||||
{my_prop.foo}
|
@ -0,0 +1,14 @@
|
||||
import { test } from '../../test';
|
||||
import { flushSync } from 'svelte';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target, logs }) {
|
||||
const [btn1] = target.querySelectorAll('button');
|
||||
|
||||
flushSync(() => {
|
||||
btn1.click();
|
||||
});
|
||||
|
||||
assert.deepEqual(logs, ['bar']);
|
||||
}
|
||||
});
|
@ -0,0 +1,15 @@
|
||||
<script>
|
||||
import Component from './Component.svelte';
|
||||
|
||||
let value = { foo: 'bar' };
|
||||
</script>
|
||||
|
||||
<button
|
||||
onclick={() => {
|
||||
value = undefined;
|
||||
}}>Reset value</button
|
||||
>
|
||||
|
||||
{#if value !== undefined}
|
||||
<Component my_prop={value} />
|
||||
{/if}
|
@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
export let ref;
|
||||
</script>
|
||||
|
||||
<input bind:this={ref} />
|
@ -0,0 +1,11 @@
|
||||
import { test } from '../../test';
|
||||
import { flushSync } from 'svelte';
|
||||
|
||||
export default test({
|
||||
async test({ target }) {
|
||||
const [btn1] = target.querySelectorAll('button');
|
||||
|
||||
btn1.click();
|
||||
flushSync();
|
||||
}
|
||||
});
|
@ -0,0 +1,16 @@
|
||||
<script>
|
||||
import Component from './Component.svelte';
|
||||
let state = { title: 'foo' };
|
||||
</script>
|
||||
|
||||
{#if state}
|
||||
{@const attributes = { title: state.title }}
|
||||
<Component {...attributes} />
|
||||
{/if}
|
||||
<button
|
||||
onclick={() => {
|
||||
state = undefined;
|
||||
}}
|
||||
>
|
||||
Del
|
||||
</button>
|
@ -0,0 +1,12 @@
|
||||
<script>
|
||||
import { onDestroy } from "svelte";
|
||||
export let checked;
|
||||
export let count;
|
||||
onDestroy(() => {
|
||||
console.log(count, checked);
|
||||
});
|
||||
</script>
|
||||
|
||||
<p>{count}</p>
|
||||
|
||||
<button onclick={()=> count-- }></button>
|
@ -0,0 +1,68 @@
|
||||
import { test } from '../../test';
|
||||
import { flushSync } from 'svelte';
|
||||
|
||||
export default test({
|
||||
async test({ assert, target, logs }) {
|
||||
const [btn1, btn2, btn3] = target.querySelectorAll('button');
|
||||
let ps = [...target.querySelectorAll('p')];
|
||||
|
||||
for (const p of ps) {
|
||||
assert.equal(p.innerHTML, '0');
|
||||
}
|
||||
|
||||
flushSync(() => {
|
||||
btn1.click();
|
||||
});
|
||||
|
||||
// prop update normally if we are not unmounting
|
||||
for (const p of ps) {
|
||||
assert.equal(p.innerHTML, '1');
|
||||
}
|
||||
|
||||
flushSync(() => {
|
||||
btn3.click();
|
||||
});
|
||||
|
||||
// binding still works and update the value correctly
|
||||
for (const p of ps) {
|
||||
assert.equal(p.innerHTML, '0');
|
||||
}
|
||||
|
||||
flushSync(() => {
|
||||
btn1.click();
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
btn1.click();
|
||||
});
|
||||
|
||||
console.warn(logs);
|
||||
|
||||
// the five components guarded by `count < 2` unmount and log
|
||||
assert.deepEqual(logs, [1, true, 1, true, 1, true, 1, true, 1, true]);
|
||||
|
||||
flushSync(() => {
|
||||
btn2.click();
|
||||
});
|
||||
|
||||
// the three components guarded by `show` unmount and log
|
||||
assert.deepEqual(logs, [
|
||||
1,
|
||||
true,
|
||||
1,
|
||||
true,
|
||||
1,
|
||||
true,
|
||||
1,
|
||||
true,
|
||||
1,
|
||||
true,
|
||||
2,
|
||||
true,
|
||||
2,
|
||||
true,
|
||||
2,
|
||||
true
|
||||
]);
|
||||
}
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
<script>
|
||||
import Component from "./Component.svelte";
|
||||
let show = true;
|
||||
let count = 0;
|
||||
$: spread = { checked: show, count };
|
||||
</script>
|
||||
|
||||
<button onclick={()=> count++ }></button>
|
||||
<button onclick={()=> show = !show }></button>
|
||||
|
||||
<!-- count with bind -->
|
||||
{#if count < 2}
|
||||
<Component bind:count bind:checked={show} />
|
||||
{/if}
|
||||
|
||||
<!-- spread syntax -->
|
||||
{#if count < 2}
|
||||
<Component {...spread} />
|
||||
{/if}
|
||||
|
||||
<!-- normal prop -->
|
||||
{#if count < 2}
|
||||
<Component {count} checked={show} />
|
||||
{/if}
|
||||
|
||||
<!-- prop only accessed in destroy -->
|
||||
{#if show}
|
||||
<Component {count} checked={show} />
|
||||
{/if}
|
||||
|
||||
<!-- dynamic component -->
|
||||
<svelte:component this={count < 2 ? Component : undefined} {count} checked={show} />
|
||||
|
||||
<!-- dynamic component spread -->
|
||||
<svelte:component this={count < 2 ? Component : undefined} {...spread} />
|
||||
|
||||
<!-- dynamic component with prop only accessed on destroy -->
|
||||
<svelte:component this={show ? Component : undefined} {count} checked={show} />
|
||||
|
||||
<!-- dynamic component with prop only accessed on destroy spread -->
|
||||
<svelte:component this={show ? Component : undefined} {...spread} />
|
Loading…
Reference in new issue