Since the first cube recalculates the size and passes it out to the parent, the bind: is needed. The second cube only 'consumes' the value without changing it, so it can be just {zoom} without the binding. Here's an even simplified example (setting the invertal in onMount and clearing it on destroy might be advisable) I think the question is
How 'detach' a component so that a prop is no longer passed, while the value inside the component is preserved (or reset to a default value?)
(If you might also want to disconnet the first cube or the second needs the binding because it modifies the value as well, I would adjust the example)
While hoping that there's a "real Svelte solution" to this, this is a workaround how I would solve your exact given example REPL
Compared to rixo's answer this doesn't implement toggling the connection, but the connection could be detached individually for every component
<script>
...
let cube2
</script>
<button on:click={() => cube2.detach()}>button</button>
<div>
<Cube1 bind:zoom />
<Cube2 {zoom} bind:this={cube2}/>
</div>
<script>
export let zoom = { width: 120, height: 120 };
let width, height, detached
$: if(!detached) {
width = zoom.width + 'px'
height = zoom.height + 'px'
}
export const detach = () => detached = true
</script>
<div class="cube" style:width style:height></div>
<style>
.cube {
background: chartreuse
}
</style>
The usage of stores in rixo's example gave me the idea of using a store and manually handle the subscription inside the component so it can be unsubscribed to detach. Here's a solution with zoom being a writable store REPL
App.svelte
<script>
import {writable} from 'svelte/store'
import Cube1 from "./Cube1.svelte"
import Cube2 from "./Cube2.svelte"
const zoom = writable({ width: 100, height: 100 })
let cube2
</script>
<button on:click={() => cube2.detach()}>
detach Cube2
</button>
<div>
<Cube1 {zoom} />
<Cube2 {zoom} bind:this={cube2}/>
</div>
Cube1.svelte
<script>
export let zoom;
setInterval(() => {
$zoom.width = 50 + Math.random() * 100;
$zoom.height = 50 + Math.random() * 100;
}
,1000)
</script>
<div class="cube"
style="width:{$zoom.width}px;height:{$zoom.height}px">
</div>
<style>
.cube {
background: grey
}
</style>
Cube2.svelte
<script>
export let zoom
let width, height
const cancelSubscription = zoom.subscribe(zoom => {
width = zoom.width + 'px'
height = zoom.height + 'px'
})
export const detach = () => cancelSubscription()
</script>
<div class="cube" style:width style:height></div>
<style>
.cube {
background: chartreuse
}
</style>
And yet another way REPL making use of <svelte:options accessors={true} />
App.svelte
<script>
import Cube1 from "./Cube1.svelte"
import Cube2 from "./Cube2.svelte"
let zoom = { width: 100, height: 100 }
const attached = {}
$: Object.values(attached).forEach(component => {
if(component) component.zoom = zoom
})
</script>
<button on:click={() => {delete attached.cube2}}>detach Cube2</button>
<div>
<Cube1 bind:zoom />
<Cube2 bind:this={attached.cube2}/>
</div>
Cube2.svelte
<svelte:options accessors={true} />
<script>
export let zoom = { width: 120, height: 120 };
$: width = zoom.width + 'px'
$: height = zoom.height + 'px'
</script>
<div class="cube" style:width style:height></div>
<style>
.cube {
background: chartreuse
}
</style>