Skip to content

Re-rendering occurs before the execution of the #if directive. #16072

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Sincenir opened this issue Jun 3, 2025 · 2 comments
Open

Re-rendering occurs before the execution of the #if directive. #16072

Sincenir opened this issue Jun 3, 2025 · 2 comments
Labels
p0 stuff we should fix ASAP
Milestone

Comments

@Sincenir
Copy link

Sincenir commented Jun 3, 2025

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

@dummdidumm
Copy link
Member

Playground of the issue

It seems that the effect inside TestChildren2.svelte does kick off the chain that leads to the error. It closes the modal, and then the render tag inside Modal.svelte is executed even though it shouldn't be because the surrounding if should've already made it falsy.

@dummdidumm dummdidumm added this to the 5.x milestone Jun 3, 2025
@dummdidumm dummdidumm added the p0 stuff we should fix ASAP label Jun 3, 2025
@Sincenir
Copy link
Author

Sincenir commented Jun 5, 2025

Playground of the issue

It seems that the effect inside TestChildren2.svelte does kick off the chain that leads to the error. It closes the modal, and then the render tag inside Modal.svelte is executed even though it shouldn't be because the surrounding if should've already made it falsy.

This problem only occurs when TestChildren2 uses $effect. The same applies when using $inspect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p0 stuff we should fix ASAP
Projects
None yet
Development

No branches or pull requests

2 participants