Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Usage of onMount inside a function

I’m going through the Svelte tutorial. I’m in the ContextAPI part, and I don’t understand why and how onMount is called again inside the addItem function, in addition to the first call, in Canvas.svelte. Isn’t it supposed to be called only once during component initialization?

Canvas.svelte:

<script>
    import { setContext, afterUpdate, onMount, tick } from 'svelte';

    export let width = 100;
    export let height = 100;

    let canvas, ctx;
    let items = new Set();
    let scheduled = false;

    onMount(() => {
        ctx = canvas.getContext('2d');
    });

    setContext('canvas', {
        addItem
    });

    function addItem(fn) {
        onMount(() => {
            items.add(fn);
            return () => items.delete(fn);
        });
        
        afterUpdate(async () => {
            if (scheduled) return;
            
            scheduled = true;
            await tick();
            scheduled = false;

            draw();
        });
    }

    function draw() {
        ctx.clearRect(0, 0, width, height);
        items.forEach(fn => fn(ctx));
    }
</script>

<canvas bind:this={canvas} {width} {height}>
    <slot />
</canvas>

<style>
    canvas {
        width: 100%;
        height: 100%;
    }
</style>

App.svelte:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

<script>
    import Canvas from './Canvas.svelte';
    import Square from './Square.svelte';

    // we use a seeded random number generator to get consistent jitter
    let seed = 1;

    function random() {
        seed *= 16807;
        seed %= 2147483647;
        return (seed - 1) / 2147483646;
    }

    function jitter(amount) {
        return amount * (random() - 0.5);
    }
</script>

<div class="container">
    <Canvas width={800} height={1200}>
        {#each Array(12) as _, c}
            {#each Array(22) as _, r}
                <Square
                    x={180 + c * 40 + jitter(r * 2)}
                    y={180 + r * 40 + jitter(r * 2)}
                    size={40}
                    rotate={jitter(r * 0.05)}
                />
            {/each}
        {/each}
    </Canvas>
</div>

<style>
    .container {
        height: 100%;
        aspect-ratio: 2 / 3;
        margin: 0 auto;
        background: rgb(224, 219, 213);
        filter: drop-shadow(0.5em 0.5em 1em rgba(0, 0, 0, 0.1));
    }
</style>

Square.svelte:

<script>
    import { getContext } from 'svelte';

    export let x;
    export let y;
    export let size;
    export let rotate;

    getContext('canvas').addItem(draw);

    function draw(ctx) {
        ctx.save();

        ctx.translate(x, y);
        ctx.rotate(rotate);

        ctx.strokeStyle = 'black';
        ctx.strokeRect(-size / 2, -size / 2, size, size);

        ctx.restore();
    }
</script>

>Solution :

Isn’t it supposed to be called only once during component initialization?

You can call it as many times as you want, it just adds an event handler to the list. But it does have to happen during component initialization.

addItem is being passed to the context and called during the initialization of the child components Square, so it refers to the mounting of that, not the parent Canvas.

In general it can be useful to use those hooks in functions to enable work that has its own initialization and cleanup logic attached to it.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading