Description
Describe the bug
If you mount a component to the DOM programmatically using mount
, and pass in a $state
object via context. reactivity to the state with {#if}
statements does not work correctly.
Playground:
https://svelte.dev/playground/63a37aca753544ba9c2f96dd5bd4cd3a?version=5.28.2
In this snippet I show something like:
{#if value === true}
{value}
{/if}
but the rendered HTML shows both "true" and "false".
Some more observations
- Duplicating this if block in the template twice makes it work
- There are problems with $derived not reacting to changing state as well
Reproduction
App.svelte
<script>
import { setContext, getContext, onMount } from 'svelte';
import { contextTest } from './service.svelte.js';
const stateObjectFromContext = getContext('stateContext')
let element = undefined
onMount(() => {
contextTest(element)
})
</script>
<div bind:this={element}></div>
service.svelte.js
import { setContext, getContext, mount } from 'svelte';
import NestedComponent from './NestedComponent.svelte'
export const contextTest = (target) => {
const stateObject = $state({
showText: true
})
mount(NestedComponent, {target, context: new Map([['stateContext', stateObject]]), props: {}})
setInterval(() => {
stateObject.showText = !stateObject.showText
}, 1000)
}
NestedComponent.svelte
<script>
import { setContext, getContext } from 'svelte';
import { ContextTest } from './service.svelte.js';
const stateObjectFromContext = getContext('stateContext')
</script>
<p>Following text is inside an 'if' statement and should only ever be able to show 'true'</p>
{#if stateObjectFromContext.showText === true}
<h1>{stateObjectFromContext.showText}</h1>
{/if}
Logs
System Info
System:
OS: Linux 5.15 Ubuntu 22.04.5 LTS 22.04.5 LTS (Jammy Jellyfish)
CPU: (20) x64 12th Gen Intel(R) Core(TM) i7-12700H
Memory: 9.89 GB / 15.49 GB
Container: Yes
Shell: 5.1.16 - /bin/bash
Binaries:
Node: 22.2.0 - ~/.nvm/versions/node/v22.2.0/bin/node
Yarn: 1.22.22 - /usr/bin/yarn
npm: 10.7.0 - ~/.nvm/versions/node/v22.2.0/bin/npm
pnpm: 10.10.0 - /usr/bin/pnpm
npmPackages:
svelte: ^5.28.2 => 5.28.2
Severity
blocking all usage of svelte
Activity
kepzAT commentedon May 6, 2025
I can narrow it down more, in this Playground it doesn't work if you modify the state from inside
service.svelte.js
but if you move the interval insideNestedComponent.svelte
if statement works.brunnerh commentedon May 6, 2025
That is not how to set context when mounting components dynamically.
If you want to pass on existing contexts, use
getAllContexts
and merge that with new contents. Also note that context functions should only be called during component initialization so if you dynamically mount components later, you often have to get context data a lot earlier and store it somewhere to pass it on.(By the way, you overwrote the state of the original reproduction, so it is unclear what the original issue looked like. I would generally recommend including code in the issue rather than just linking to the playground.)
kepzAT commentedon May 6, 2025
My bad for accidentally overwriting, reverted to the example!
And here is the code:
App.svelte
service.svelte.js
NestedComponent.svelte
With your suggestion it still does not work for me unfortunately.
brunnerh commentedon May 6, 2025
Strange bug. Adding an
$inspect(stateObjectFromContext);
also seems to fix the#if
but the inspect does not track any of the changes to the object (only showing theinit
entry)...7nik commentedon May 6, 2025
Wrapping
mount
in$effect
fixes the issue. And creating thestateObject
not duringonMount
, e.g. in<script>
, also fixes.brunnerh commentedon May 6, 2025
I don't think adding another
$effect
should be necessary.Doing some playground bisection, the issue seems to have been introduced in v.5.24.0
Playground @ 5.23.2
Playground @ 5.24.0
Maybe it has to do with this change:
Seen some other issues referencing that in particular.
raythurnvoid commentedon Jun 2, 2025
The commit that introduced the issue is:
fix: don't depend on deriveds created inside the current reaction
dummdidumm commentedon Jun 10, 2025
Confirmed that this is due to #15553, the wrong effect is set as active so updates are not notified.
Update: The problem is that
reaction_sources
is pushed to, and then inget
it says "oh there's the same signal inreaction_sources
so I'm gonna skip it", even though we're not executed in the context of the reaction the signal was created in. So the check needs to be enhanced to see if we're inside the same reaction.with_parent
in proxy #16176fix: ensure sources within nested effects still register correctly
2 remaining items