Search code examples
sveltesvelte-3svelte-component

Svelte: How to pass multiple actions to a component?


I've learnt that it's possible to pass an action to a component: https://stackoverflow.com/a/66142037/15943057. I wonder if it would be possible to pass multiple (any number of) actions to a single component?

Edit: I've tried to write an action that would apply actions passed as an argument, and it didn't work. But I just saw this comment, so I suppose it is possible. Perhaps my mistake was that I tried to add an attribute the node.setAttribute(...) way, maybe it could be done the Svelte way?


Solution

  • You could combine the actions into one single action, e.g.

    <script>
        export let actions;
        
        const combinedActions = node => {
            const destructors = actions.map(
                ({ action, params }) => action(node, params)
            );
            
            return {
                destroy() {
                    destructors.forEach(destructor => {
                        if (typeof destructor?.destroy == 'function')
                            destructor.destroy();
                    })
                }
            };
        }
    </script>
    
    <div use:combinedActions >
        Child component
    </div>
    

    Usage example:

    <script>
        import Child from './Child.svelte';
        
        const color = (node, params) => node.style.color = params;
        const bg = node => {
            node.style.background = 'grey';
            
            return { destroy() { console.log('destroyed') } };
        }
        
        let checked = true;
    </script>
    
    <label>
        <input bind:checked type=checkbox />
        mounted
    </label>
    
    {#if checked}
        <Child actions={[
                     { action: color, params: 'red' },
                     { action: bg }
                ]} />
    {/if}
    

    REPL