Vue Ref nolonger reactive after being returned from composable

I am working with Nuxt 3 composition API using the Vue 3 script setup syntactic sugar.

I have an issue where refs from my composable isn’t reactive once it has been returned to a component. Watching the value has no effect.

Take note that I am using VueUse to create different instances of the filedialog (useFileDialog).

import { useFileDialog } from '@vueuse/core'
import { ref as storageRef, getStorage, listAll, getDownloadURL, deleteObject } from 'firebase/storage'
import { useStorageFile } from 'vuefire'
import { ref, computed, watch } from 'vue'

export const useFileUpload = (props) => {
    const storage = getStorage();
    const filename = ref();
    const fileRef = ref(null);
    const selectedImages = ref([]);
    const images = ref({});
    const imagePreviews = computed(() => {
        if (images.value) {
            return Array.from(images.value).map(file => URL.createObjectURL(file));
        }
        return [];
    })

    const {
        url,
        uploadTask,
        upload,
    } = useStorageFile(fileRef)

    const { files, open } = useFileDialog()
    const { open: openMaterialer, files: materialerFiles } = useFileDialog();
    const { open: openPlantegning, files: plantegningFiles } = useFileDialog();

    watch(files, (newFiles) => {
        if (newFiles.length > 0) {
            const fileType = newFiles[0].type.split("https://stackoverflow.com/")[0]
            if (fileType === 'image') {
                images.value = newFiles
            }
            else return
        }
    })

    const uploadFile = async () => {
// Some upload logic here
}
        

    watchEffect(() => {
        // Confirms reactivity in composable
        console.log('materialerFiles in upload:', materialerFiles.value);
      });

    return {
        imagePreviews,
        files,
        materialerFiles,
        plantegningFiles,
        uploadTask,
        open,
        openMaterialer,
        openPlantegning,
        uploadFile,
    };
};

Now in my component i use the composable (remember that the composable is auto imported)

    // files
    const { files, materialerFiles, plantegningFiles } = useFileUpload()
    
    // Watch for changes to materialerFiles
    watch(materialerFiles, (newMaterialerFiles) => {
      console.log('changed')
        userInput.value = {
            ...userInput.value,
            materialerFiles: newMaterialerFiles,
        };
    });

This should log ‘changed’ to the console from the component. But nothing happens.

Have i misunderstood something or is this a known issue. Any workarounds or suggestions?

I expect to log the files which should materilaerFiles should contain.

Steps to reproduce:

  1. Create a composable in the “composable” directory
  2. Import useFileDialog from vueUse and ref,watch, computed from vue
  3. Inside the composable create 3 instances of the fileDialog
  4. Return the reactive file values from the composable
  5. in a component deconstruct the composable const {returned values here} = useYourComposable()
  6. Watch for changes in one or more of the returned values

  • Please, provide a way to reproduce, see stackoverflow.com/help/mcve . Probably something that is specific to autoimports. There’s absolutely no difference if watch is called inside custom composable or outside it, it’s just JS function without magical properties.

    – 

  • Explicitly importing the composable does not change the result.

    – 

  • Please, provide a demo. I expect that it won’t be possible to reproduce with the provided steps for the reason mentioned above. Notice that the difference is that watchEffect runs immediately and watch doesn’t. In case there was ‘materialerFiles in upload’ but not ‘changed’, that’s why

    – 




  • There is no difference in behavior when switching watchEffect out with a normalt watch or the other way around. I get the same result. Working on the demo

    – 

  • As far as I can tell the issue lies with the composable from VueUse. All refs i create myself change reactively as expected. But specifically the values deconstructed from usefileDialog does not react to changes once imported in a component

    – 

Currently you are setting a ref in userInput.value.materialerFiles, not the value of newMaterialerFiles ref.

Try update you watch by setting userInput.value using the value of materialerFiles ref like this:

// files
const { files, materialerFiles, plantegningFiles } = useFileUpload()

// Watch for changes to materialerFiles
watch(materialerFiles, (newMaterialerFiles) => {
  console.log('changed')
    userInput.value = {
        ...userInput.value,
        materialerFiles: newMaterialerFiles.value,
    };
});

Leave a Comment