I am using a composable to load images in Vue3. I have been able to pass all the props successfully as one object, see this question, but I am unable to pass the one property I want to be reactive. I am fairly certain that the issue is that the property in undefined
// loadImage.js
import { onMounted, ref, watch } from 'vue'
// by convention, composable function names start with "use"
export function useLoadImage(src) {
let loading = ref(true)
let show = ref(false)
const delayShowImage = () => {
setTimeout(() => {
show.value = true
}, 100)
}
const loadImage = (src) => {
let img = new Image()
img.onload = (e) => {
loading.value = false
img.onload = undefined
img.src = undefined
img = undefined
delayShowImage()
}
img.src = src
}
onMounted(() => {
if (src) {
loadImage(src)
}
})
watch(
() => src,
(val) => {
if (val) {
loading.value = true
loadImage(val)
}
},
)
// expose managed state as return value
/**
* loading is the image is loading
* show is a delayed show for images that transition.
*/
return { loading, show }
}
The below method returns this in the console.log and does not error.
Proxy {src: undefined} undefined
<script setup>
import { defineProps, computed } from 'vue'
import { useLoadImage } from '../../composables/loadImage'
const props = defineProps({
src: String
})
console.log(props, props.src)
const srcRef = computed(() => props.src)
const { loading, show } = useLoadImage(srcRef)
</script>
The below method returns this in the console.log
Proxy {src: undefined} undefined
and gives the following error
TypeError: Cannot read properties of undefined (reading 'undefined')
<script setup>
import { defineProps, toRef } from 'vue'
import { useLoadImage } from '../../composables/loadImage'
const props = defineProps({
src: String
})
console.log(props, props.src)
const srcRef = toRef(props.src)
const { loading, show } = useLoadImage(srcRef)
</script>
CodePudding user response:
As indicated in comments, it seems src
is undefined
in your component because you're probably not passing the prop correctly to the component.
Even if src
were set with a string, there still would be a few other issues:
toRef
's first argument should be areactive
object (i.e.,props
), and the second argument should be the name of a key (i.e.,'src'
):// MyComponent.vue const srcRef = toRef(props.src) ❌ const srcRef = toRef(props, 'src') ✅
Note: It's also valid to use
const srcRef = computed(() => props.src)
, as you were originally doing.watch
's first argument is aWatchSource
. WhenWatchSource
is a function dealing with aref
, it should return theref
's unwrapped value. Alternatively, theWatchSource
can be theref
itself:// loadImage.js watch(() => srcRef, /* callback */) ❌ watch(() => srcRef.value, /* callback */) ✅ watch(srcRef, /* callback */) ✅
The composable receives the image source in a
ref
, and youronMounted()
hook is passing thatref
toloadImage()
, which is actually expecting the string in theref
's unwrapped value:// loadImage.js onMounted(() => { if (src) { ❌ /* src is a ref in this composable */ loadImage(src) } }) onMounted(() => { if (src.value) { ✅ loadImage(src.value) } })