Description
Describe the bug
I have a Modal Component that provides the style of Modal, content slot, and methods to open and close Modal.
The control method of opening and closing is implemented through the state of current,and current is set to undefiend
when closing.
When calling this component, if there are child components within chilren, and first-level child component has an $effect, and second-level child component calls the clse methods, an exception whill be thrown.
Uncaught TypeError: Cannot read properties of undefined (reading 'model')
I have a #if
check for the data usage, but the error still occurs. I suspect that the child components'rerendering executes earlier than the #if
check.
test.svelte.ts(data model):
type State = 'init' | 'submitted';
export function createModal() {
let state = $state<State>('init');
return {
get state() {
return state;
},
submit() {
state = 'submitted';
}
};
}
export type Model = ReturnType<typeof createModal>;
Modal.svelte:
<script lang="ts" module>
let current = $state<{ model: Model }>();
export function open(model: Model) {
current = {
model
};
}
export function close() {
current = undefined;
}
</script>
<script lang="ts">
import { onDestroy, type Snippet } from 'svelte';
import type { Model } from './test.svelte';
let {
children
}: {
children: Snippet<[Model]>;
} = $props();
onDestroy(() => {
console.log('destroy test');
});
</script>
{#if current?.model}
{@render children(current.model)}
{/if}
+page.svelte
<script>
import { createModal } from './test.svelte';
import TestChildren1 from './TestChildren1.svelte';
import TestComponent, { open } from './Modal.svelte';
const model = createModal();
$effect(() => {
console.log('model', model);
});
</script>
<button
onclick={() => {
open(model);
}}>open</button
>
<TestComponent>
{#snippet children(model)}
<TestChildren1 {model} />
{/snippet}
</TestComponent>
TestChildren1:
<script lang="ts">
import { onDestroy } from 'svelte';
import TestChildren2 from './TestChildren2.svelte';
import type { Model } from './test.svelte';
let {
model
}: {
model: Model;
} = $props();
$effect(() => {
console.log('b', JSON.stringify(model));
});
onDestroy(() => {
console.log('destroy component 1');
});
</script>
<span>{model.state}</span>
<TestChildren2 {model} />
TestChildren2:
<script lang="ts">
import { onDestroy } from 'svelte';
import { close } from './Modal.svelte';
import type { Model } from './test.svelte';
let {
model
}: {
model: Model;
} = $props();
$effect(() => {
if (model.state === 'submitted') close();
});
setTimeout(() => {
model.submit();
}, 1000);
onDestroy(() => {
console.log('destroy component 2');
});
</script>
<span>{model.state}</span>
Reproduction
example:https://github.com/Sincenir/svelte-issues-effect-and-modal
localhost... /test2
Logs
System Info
chunk-ZPWZ3TV6.js?v=a4869f9a:1549 Uncaught TypeError: Cannot read properties of undefined (reading 'model')
in $effect
in TestChildren1.svelte
in Modal.svelte
in +page.svelte
in +layout.svelte
in root.svelte
at Modal.svelte:29:27
at get model (+page.svelte:19:26)
at $effect (TestChildren1.svelte:13:34)
(匿名) @ Modal.svelte:29
get model @ +page.svelte:19
$effect @ TestChildren1.svelte:13
setTimeout
TestChildren2 @ TestChildren2.svelte:16
TestChildren1 @ TestChildren1.svelte:18
(匿名) @ +page.svelte:19
consequent @ Modal.svelte:25
(匿名) @ Modal.svelte:28
Severity
blocking an upgrade