Skip to content

Reactive statement cleanup function, like useEffect return value in React #5283

Closed
@DylanVann

Description

@DylanVann

Is your feature request related to a problem? Please describe.

I'm trying to port some code that uses React hooks to Svelte. React has useEffect, which can be used to run some code when its dependencies change. I think the Svelte equivalent would be reactive statements ($: { doStuff() }).

When using useEffect you can return a function, and that function will be called when dependencies change, or when the component using the useEffect is unmounted. I do not see an equivalent in Svelte.

Here are the relevant docs for useEffect: https://reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect

Describe the solution you'd like

I think it might be beneficial for Svelte to allow for returning cleanup functions in reactive statements.
In order to allow for returning though I think it would need to be possible to give a function as a reactive statement. Not sure, I'm hoping something like this could be possible though.

// A prop. Some kind of track object that can have listeners.
export let track

// We want to stop all tracks when our stop event listener is called.
// When the track prop is changed to a different track, or when the component is unmounted
// we want to remove the listener.
// This must be a function so we can use a return statement. The function should be called as if it was a statement in the same place.
$: () => {
  let onStop = () => track.stopAllTracks()
  track.on('stop', onStop)
  return () => {
    track.off('stop', onStop)
  }
}

For reference in React this would look like:

useEffect(() => {
  let onStop = () => track.stopAllTracks()
  track.on('stop', onStop)
  return () => {
    track.off('stop', onStop)
  }
}, [track])

Describe alternatives you've considered

The closest I could come up with is this:

import { onDestroy } from 'svelte'
    
export let track
    
let cleanup
    
$: {
  if (cleanup) {
    cleanup()
  }
  const onStop = () => track.stopAllTracks()
  track.on('stop', onStop)
  cleanup = () => track.off('stop', onStop)
}
    
onDestroy(() => {
  if (cleanup) {
    cleanup()
  }
})

This is verbose compared to how useEffect works. As a React user this seems like a step backwards, feeling more like class components, lifecycle methods, instance variables, instead of clean like the hooks version.

This could be cleaned up a bit by initializing cleanup:

import { onDestroy } from 'svelte'
    
export let track
    
let cleanup = () => {}
$: {
  cleanup()
  const onStop = () => track.stopAllTracks()
  track.on('stop', onStop)
  cleanup = () => track.off('stop', onStop)
}
onDestroy(cleanup)

How important is this feature to you?

It's important to me because I'm trying to convert React code to Svelte code and there doesn't seem to be a clean translation of this common React feature (useEffect + cleanup function).

I believe many other users may come from the React ecosystem and encounter this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions