reset should just be a noop after the first call

pull/16171/head
Rich Harris 3 months ago
parent 28b0310e35
commit 0ebb878166

@ -300,6 +300,32 @@ Reactive `$state(...)` proxies and the values they proxy have different identiti
To resolve this, ensure you're comparing values where both values were created with `$state(...)`, or neither were. Note that `$state.raw(...)` will _not_ create a state proxy. To resolve this, ensure you're comparing values where both values were created with `$state(...)`, or neither were. Note that `$state.raw(...)` will _not_ create a state proxy.
### svelte_boundary_reset_noop
```
A `<svelte:boundary>` `reset` function only resets the boundary the first time it is called
```
When an error occurs while rendering the contents of a [`<svelte:boundary>`](https://svelte.dev/docs/svelte/svelte-boundary), the `onerror` handler is called with the error plus a `reset` function that attempts to re-render the contents.
This `reset` function should only be called once. After that, it has no effect — in a case like this, where a reference to `reset` is stored outside the boundary, clicking the button while `<Contents />` is rendered will _not_ cause the contents to be rendered again.
```svelte
<script>
let reset;
</script>
<button onclick={reset}>reset</button>
<svelte:boundary onerror={(e, r) => (reset = r)}>
<!-- contents >
{#snippet failed(e)}
<p>oops! {e.message}</p>
{/snippet}
</svelte:boundary>
```
### transition_slide_display ### transition_slide_display
``` ```

@ -262,6 +262,30 @@ To silence the warning, ensure that `value`:
To resolve this, ensure you're comparing values where both values were created with `$state(...)`, or neither were. Note that `$state.raw(...)` will _not_ create a state proxy. To resolve this, ensure you're comparing values where both values were created with `$state(...)`, or neither were. Note that `$state.raw(...)` will _not_ create a state proxy.
## svelte_boundary_reset_noop
> A `<svelte:boundary>` `reset` function only resets the boundary the first time it is called
When an error occurs while rendering the contents of a [`<svelte:boundary>`](https://svelte.dev/docs/svelte/svelte-boundary), the `onerror` handler is called with the error plus a `reset` function that attempts to re-render the contents.
This `reset` function should only be called once. After that, it has no effect — in a case like this, where a reference to `reset` is stored outside the boundary, clicking the button while `<Contents />` is rendered will _not_ cause the contents to be rendered again.
```svelte
<script>
let reset;
</script>
<button onclick={reset}>reset</button>
<svelte:boundary onerror={(e, r) => (reset = r)}>
<!-- contents >
{#snippet failed(e)}
<p>oops! {e.message}</p>
{/snippet}
</svelte:boundary>
```
## transition_slide_display ## transition_slide_display
> The `slide` transition does not work correctly for elements with `display: %value%` > The `slide` transition does not work correctly for elements with `display: %value%`

@ -74,7 +74,16 @@ export function boundary(node, props, boundary_fn) {
throw error; throw error;
} }
var did_reset = false;
var reset = () => { var reset = () => {
if (did_reset) {
w.svelte_boundary_reset_noop();
return;
}
did_reset = true;
pause_effect(boundary_effect); pause_effect(boundary_effect);
with_boundary(boundary, () => { with_boundary(boundary, () => {

@ -43,6 +43,17 @@ export function console_log_state(method) {
} }
} }
/**
* A `<svelte:boundary>` `reset` function only resets the boundary the first time it is called
*/
export function svelte_boundary_reset_noop() {
if (DEV) {
console.warn(`%c[svelte] svelte_boundary_reset_noop\n%cA \`<svelte:boundary>\` \`reset\` function only resets the boundary the first time it is called\nhttps://svelte.dev/e/svelte_boundary_reset_noop`, bold, normal);
} else {
console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`);
}
}
/** /**
* %handler% should be a function. Did you mean to %suggestion%? * %handler% should be a function. Did you mean to %suggestion%?
* @param {string} handler * @param {string} handler

@ -7,52 +7,22 @@ export default test({
<button>toggle</button> <button>toggle</button>
`, `,
expect_unhandled_rejections: true, async test({ assert, target, warnings }) {
const [btn] = target.querySelectorAll('button');
async test({ assert, target, warnings, window }) { flushSync(() => btn.click());
// @ts-expect-error
const __expected_error = (window.__expected_error = { v: false });
window.addEventListener('error', (e) => {
// @ts-expect-error when in hydrate mode we can't access variables in the scope
const __expected_error = window.__expected_error;
if (__expected_error.v) {
assert.include(e.error.message, 'error on template render');
} else {
assert.fail('Error was not expected: ' + e.error.message);
}
e.preventDefault();
});
const btn = target.querySelector('button');
// 1st click — error caught, fallback visible
btn?.click();
flushSync();
assert.htmlEqual(target.innerHTML, `<div>err</div><button>toggle</button>`); assert.htmlEqual(target.innerHTML, `<div>err</div><button>toggle</button>`);
assert.deepEqual(warnings, []);
// 2nd click — reset succeeds, normal render flushSync(() => btn.click());
btn?.click(); assert.htmlEqual(target.innerHTML, `normal content <button>toggle</button>`);
flushSync(); assert.deepEqual(warnings, []);
assert.htmlEqual(
target.innerHTML,
`
normal content
<button>toggle</button>
`
);
// 3rd click — mount-time crash escapes, boundary empty flushSync(() => btn.click());
__expected_error.v = true; assert.htmlEqual(target.innerHTML, `<div>err</div><button>toggle</button>`);
btn?.click();
flushSync();
__expected_error.v = false;
// Check that the warning is being showed to the user
assert.include(warnings[0], 'reset() was invoked');
// boundary content empty; only button remains assert.deepEqual(warnings, [
assert.htmlEqual(target.innerHTML, `<button>toggle</button>`); 'A `<svelte:boundary>` `reset` function only resets the boundary the first time it is called'
]);
} }
}); });

Loading…
Cancel
Save