·
2 min read

Vue Tip: Use effectScope for Managing and Cleaning Up Reactive Effects

MH

Michael Hoffmann

@mokkapps

Vue Tip: Use effectScope for Managing and Cleaning Up Reactive Effects Image

Vue provides the effectScope API to group and cleanup multiple reactive effects.

An EffectScope instance can automatically collect effects run within a synchronous function so that these effects can be disposed together at a later time.

Let's take a look at a simple example:

function.ts
// effect, computed, watch, watchEffect created inside the scope will be collected

const scope = effectScope()

scope.run(() => {
  const doubled = computed(() => counter.value * 2)

  watch(doubled, () => console.log(doubled.value))

  watchEffect(() => console.log('Count: ', doubled.value))
})

// Dispose of all effects in the scope
scope.stop()

Real World Example

A practical application is the createSharedComposable function in VueUse:

createSharedComposable.ts
import type { EffectScope } from 'vue'
import type { AnyFn } from '../utils'
import { effectScope } from 'vue'
import { tryOnScopeDispose } from '../tryOnScopeDispose'

/**
 * Make a composable function usable with multiple Vue instances.
 *
 * @see https://vueuse.org/createSharedComposable
 */
export function createSharedComposable<Fn extends AnyFn>(composable: Fn): Fn {
  let subscribers = 0
  let state: ReturnType<Fn> | undefined
  let scope: EffectScope | undefined

  const dispose = () => {
    subscribers -= 1
    if (scope && subscribers <= 0) {
      scope.stop()
      state = undefined
      scope = undefined
    }
  }

  return <Fn>((...args) => {
    subscribers += 1
    if (!scope) {
      scope = effectScope(true)
      state = scope.run(() => composable(...args))
    }
    tryOnScopeDispose(dispose)
    return state
  })
}

This function creates a shared composable that efficiently manages side effects across multiple components. For example, using a useMouse hook, you can prevent multiple event listeners from being added:

useMouse.ts
function useMouse() {
  const x = ref(0)
  const y = ref(0)

  function handler(e) {
    x.value = e.x
    y.value = e.y
  }

  window.addEventListener('mousemove', handler)

  onUnmounted(() => {
    window.removeEventListener('mousemove', handler)
  })

  return { x, y }
}

const useSharedMouse = createSharedComposable(useMouse);

Conclusion

effectScope allows developers to manage reactive effects more efficiently and clearly. By leveraging this powerful feature, you can ensure your applications are not only more performant but also easier to maintain.

If you liked this Vue tip, follow me on BlueSky to get notified about new tips, blog posts, and more. Alternatively (or additionally), you can subscribe to my weekly Vue & Nuxt newsletter :

I will never share any of your personal data. You can unsubscribe at any time.