question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Use actions on Svelte components

See original GitHub issue

Describe the problem

There’s some things I often find myself doing:

  • dispatching events when component is mounted:
import { onMount, createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
onMount(() => dispatch("mount"));
  • dispatching events when destroying:
import { onMount, createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
onDestroy(() => dispatch("destroy"));

E.g. I have one component WithShortcut, which exports a let: binding to create a shortcut, e.g. like this:

<WithShortcut shortcut="Control+C" let:createShortcut let:shortcutLabel>
    <Button title={shortcutLabel} on:click={doThing} on:mount={(event) => createShortcut(event.detail.button)}>Click me!</Button>
</WithShortcut>

That’s quite the boilerplate, imo, and it leads to code which doesn’t feel good, as we’re using events, just to be able to listen to the lifecycle event outside of the component. Also, using on: feels more correct, if you’re reacting to events, but what we’re doing here is extending the component, for which use: would feel better.

There’s other issues here. The cleanup for the button happens in Button, but the cleanup code for the shortcut happens in WithShortcut.

Describe the proposed solution

What would be much nicer imo, is if we could use use: actions on components. The semantics would be similar to HTML elements: the action’smount corresponds to onMount, and destroy corresponds to onDestroy.

This means that the whole thing is basically just syntax sugar. I’ve built an example here, where I’m recreating a Component use: action with props:

UseAction.svelte REPL

I’ll also copy it over here. <MyComponent use:action={param} /> translates to:

// MyComponent.svelte
<script>
  import { onMount } from "svelte";
  import { get_current_component } from "svelte/internal"
  
  export let action;
  export let param;
  
  let elementUpdate;
  
  function update() {
    if (elementUpdate) {
      elementUpdate(param);
    }
  }
  
  $: param, update();
  
  const component = get_current_component();
  
  onMount(() => {
    const { destroy, update } = action(component, param);
    elementUpdate = update;
    return destroy;
  })
</script>

The above code could then be rewritten as (I know {@const is not a thing yet, but it’s also a nice feature):

<WithShortcut shortcut="Control+C" let:createShortcut let:shortcutLabel>
    {@const createButtonShortcut = component => createShortcut(component.state.button)}
    <Button title={shortcutLabel} use:createButtonShortcut on:click={doThing}>Click me!</Button>
</WithShortcut>

See here for a REPL of how I wish I could do it.

I believe this makes it more semantic.

This would also be easy to teach new users, as it mirrors how they behave on elements:

<div bind:this={myElement} use:actionOnElement />
<Component bind:this={myComponent} use:actionOnComponent />

(This feature seems quite natural to me, and I was surprised to have not found this feature suggestion. If it has been suggested before, I’d appreciate a link.)

Alternatives considered

  1. As I mentioned above, I currently use onMount(() => dispatch("mount", { button }));. See here for a REPL.

  2. One might say, I could import registerShortcut from Button.svelte and use it there directly, but I don’t want to teach every component that might get a shortcut how to create them, and eventually how to assign tooltips (title).

Importance

would make my life easier

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:9 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
hgieselcommented, Sep 29, 2021

I think this sounds more like mixins than actions.

I always thought of actions as mixins. Even the actions used in the tutorial could be aptly described as Pannable and LongPressable.

you can get pretty far with svelte:component and passing component constructors around and spicing up props along the way

Yep, we experimented with this, but what we found is that you loose most of Svelte’s niceness like slots, and instead you’ll pass around deeply nested objects.

2reactions
Prinzhorncommented, Sep 27, 2021

I think this sounds more like mixins than actions 🤔, not sure what others are doing in this space (you can get pretty far with <svelte:component> and passing component constructors around and spicing up props along the way).

We want our Svelte app to be extensible. Doing so is quite the challenge, and having use: actions on components would make it certainly easier.

Here’s an example. I have a simple List component displaying some Item components. So we (the app developer) might have our own reasons for showing or hiding some of the items, for which we would change the visible prop. But we also want to provide a way for add-on devs to dynamically show or hide other names, which is why we want to expose APIs alongside the components, that can be used from outside. In the REPL I put this code into setTimeout.

Now this is getting too complex for discussing this here and these type of architectural decisions require more in depth understanding than what I can provide here. I will say this: I personally would think of the extensions in terms of data, not in terms of UI elements. My UIs are data/store driven. The UI is just a way to visualize the data. Your data could flow through all of of the extensions and the extensions can make decisions (e.g. setting visible to false). Like middlewares in a Connect/Express/Polka app. And the UI doesn’t even know about all this, it just updates with the current state and makes sure it’s consistent.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Actions / The use directive • Svelte Tutorial
Actions are essentially element-level lifecycle functions. They're useful for things like: interfacing with third-party libraries; lazy-loaded images ...
Read more >
Introduction to Svelte Actions - LogRocket Blog
Actions are one of Svelte's less commonly used features. An action allows you to run a function when an element is added to...
Read more >
Svelte: How to pass action to component? - Stack Overflow
Action can only be applied to DOM element. However, it's possible to pass a function by property to a component, and this component...
Read more >
Unlocking the power of Svelte Actions | Writing - Kirill Vasiltsov
Actions are essentially functions that are executed when an element is mounted. In other words, as soon as your HTML element like a...
Read more >
Implement use:Component actions. · Issue #1322 - GitHub
Actions seem like a very easy way to use a component on an object, and would be very useful to implement components that...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found