Skip to content

reactive actions #15243

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pjebs opened this issue Feb 8, 2025 · 7 comments
Closed

reactive actions #15243

pjebs opened this issue Feb 8, 2025 · 7 comments

Comments

@pjebs
Copy link

pjebs commented Feb 8, 2025

Describe the problem

Can we reconsider the restriction against the reactivity of actions from argument changes. Seems to go against the philosophy of Svelte and is effecting a library I am building.

The docs: https://svelte.dev/docs/svelte/use state:

<script lang="ts">
	import type { Action } from 'svelte/action';

	const myaction: Action = (node, data) => {
		// ...
	};
</script>

<div use:myaction={data}>...</div>

The action is only called once — it will not run again if the argument changes.

Prior to the $effect rune, actions could return an object with update and destroy methods, where update would be called with the latest value of the argument if it changed. Using effects is preferred.

I am trying to convert y-direction mouse wheel scrolls into the x-direction on desktop sites, but not have any effect on mobile devices. On Mobile sites, I pass disable=true. When the screen resizes to small I consider it a "mobile device"

Simplified example below:

const scrollEvents: Action = (node: HTMLElement, disable: boolean) => {

  let wheelHandler = (event) => {
    // Wheel Inversion
    if (disable) {
      return;
    }
    event.preventDefault();
    node.scrollLeft += (event.deltaY+ event.deltaX);
  }

  $effect(() => {
    node.addEventListener("wheel", wheelHandler);
    return () => {
      node.removeEventListener("wheel", wheelHandler);
    };
  });
}
<span use:scrollEvents={isMobile} >       <!------ state changes on `window.onresize` or `ResizeObserver`

Describe the proposed solution

Make actions reactive upon argument changes

Importance

nice to have

@paoloricciuti
Copy link
Member

Sorry I don't understand the issue here...you can still return an object containing update that will be invoked when disabled changes (specifically because it's a primitive value)...but you can also read disabled inside the effect to make it rerun.

How is this affecting your library?

@adiguba
Copy link
Contributor

adiguba commented Feb 8, 2025

Hello,

If you want reactivity, you have to pass function that return the updated value.
Note also :

import { on } from 'svelte/events';

const scrollEvents: Action = (node: HTMLElement, is_disabled: ()=>boolean) => {

  let wheelHandler = (event) => {
    // Wheel Inversion
    event.preventDefault();
    node.scrollLeft += (event.deltaY+ event.deltaX);
  }

  $effect(() => {
    if (!is_disabled()) {
       return on(node"wheel", wheelHandler);
    }
  });
}

But you have to pass a function and not the value on the caller side :

   <span use:scrollEvents={() => isMobile} >

Another solution would be to use the legacy API to handle that, by returning an object with an update method :
Exemple :
https://svelte.dev/playground/hello-world?version=5.19.9#H4sIAAAAAAAACnVUXW-bMBT9K3dkUhM1TVJtkyYaUlVapT60b3vYNKbJ4Au4dWxkm3wI8d9nG5OStUsejM89vvfcwzVtJMgWozh6QM4l7KXiFKZImUE6i-ZRwTjqKP7VRuZYO54DLB5O3dX1Qu-QG4dlRON7eC6FQWFsmmitc8VqA5yIMkkjo9Nok4rUsG0tlQFXA1q4yw2TAjoolNzCRZ9oSTx6cTPit_CWhjtXy9MskaMBpp9kZmVDAh-1IQanBeEaZ55ixWkDVpbk_N4fjYf6CUyFpBjDw_enx3uOWxudA2WaZNyimZQciZhBsoHWpQJw1fYVIn8ggnJULoXXM5DA_jywqJVfv2FBGm6mTowLuoKLXs0jFgYuhwwLarsjPy9htPvRn-peW80V2v5G1U_qTGoUmkaJoQG4BdFwDrH10Pc5hzTy4tNoftaFN8qMqii0jWvn51m96SyYHgqFsk1NnecC93_eehc49o0WMKbAhyQZlI5YqRniCYzYN6_xoO120as5R99X7BndiezXbt6vFLVR8jgdaXhboQv-eIvWy37GN37DSYZ8s2aibvrxtlOfV5i_ZPKQRpAxQWO_R5q0w6B2sNxAmNn1sk_hs1G2g0ZjPB7X0TF_ldoJkryClqMoTRXD9WrVzYF1_489h97X2aZ9vmTdepn1mZaO7YPrTC3_wWyjVo7XZZ-1OXL0DKcxeCV3qAou93G4X8GuPaOu-NfVbh-QCllZGQ9VAcpI_lIq2Vh_YFJcu_9wvLIfpytdk9wOkr1GGtUO7cXZK1KfJtVkgwg7IjUnxxiY4EzgVcZl_hJS1YRSJsoYVrBafMHtUFsqisraUx9AS84oTPI8P1f_6cQ2eDBXhLNSxJDbN4IqBLZElcyCn-vDoMsOhzfKfhXdsSg2qsHud_cXty7R9YcFAAA=

There should be a new API to replace the actions's API in the future...

@pjebs
Copy link
Author

pjebs commented Feb 8, 2025

@adiguba @paoloricciuti Is my issue related to #6942?
It seems like it, in which case close issue.

Thanks @adiguba for the code. That is an ugly workaround but beautiful solution on your part. I can't wait for action's API to get updated.

@pjebs
Copy link
Author

pjebs commented Feb 8, 2025

@adiguba Does your code snippet change if the argument is actually an object:

interface Options {
  disable: boolean;
  wheelInversion: boolean;
}

const scrollEvents: Action = (node: HTMLElement, options: Options) => {

<span use:scrollEvents={() => isMobile} >

In your code snippet, it doesn't use that.

@paoloricciuti
Copy link
Member

@adiguba @paoloricciuti Is my issue related to #6942?
It seems like it, in which case close issue.

Not really...that issue is about dynamically change the variable containing the function that is the action and re register it on the element.

Your issue is about how to get updates to the argument of an action but as I've said that is already possible, either with the update method like @adiguba showed you or with an effect

@pjebs
Copy link
Author

pjebs commented Feb 8, 2025

@paoloricciuti How do you do with an effect?

@paoloricciuti
Copy link
Member

@paoloricciuti How do you do with an effect?

Like this

@pjebs pjebs closed this as completed Feb 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants