Ref in composable is not reactive when placed inside returning function and works when placed outside of it

Viewed 512

I use Vue 2 with @vue/composition-api plugin.

I made a simple composable:

import { ref } from '@vue/composition-api'

export function useRfp() {
  const files = ref([])

  return {
    files
  }
}

And I update the files array in components that use this composable like this: files.value = ...

However, it works only when the composable looks like this:

import { ref } from '@vue/composition-api'

const files = ref([]) // <-- Notice the const is outside the useRfp function

export function useRfp() {
  return {
    files
  }
}

I don't understand why that happens, since in VueJS documentation they have an example with mouse tracker that uses refs inside the function:

// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'

// by convention, composable function names start with "use"
export function useMouse() {
  // state encapsulated and managed by the composable
  const x = ref(0)
  const y = ref(0)

  // a composable can update its managed state over time.
  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  // a composable can also hook into its owner component's
  // lifecycle to setup and teardown side effects.
  onMounted(() => window.addEventListener('mousemove', update))
  onUnmounted(() => window.removeEventListener('mousemove', update))

  // expose managed state as return value
  return { x, y }
}

This makes sense since it's incapsulated inside the function.

By "doesn't work" I mean that when I update the value in one component, it's still empty in another. And it doesn't matter if it's an array or a string.

Why does that happen? I feel like I shouldn't place refs outside the function.

2 Answers

I had this problem too in the past, where I needed to build a reusable alert component with data in a composable.
From what I learned, the ref/reactive values inside the function will always start with those values since they have a default value in the function.
But if you put those values outside the values will persist, like a basic state or store.
When you need to access those values later somewhere else, they won't be reset everytime you call the composable

In fact, the vue example with mouse coords will always start at {0,0} because they get reinitialized whe you call that composable.

They also mentioned this in the document:

Each component instance calling useMouse() will create its own copies of x and y state so they won't interfere with one another.

The files array you have in the component will update it's value and keep it's reactivity just fine inside that component. The other components that use the same composable will have their own files array so they are not the same thus not having the same value.

Putting it outside of the composable function will make all the components use the same instance of that files array so the value of that array is the same in every components.

Related