Reputation: 7447
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>
Upvotes: 2
Views: 2672
Reputation: 138326
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 a reactive
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 a WatchSource
. When WatchSource
is a function dealing with a ref
, it should return the ref
's unwrapped value. Alternatively, the WatchSource
can be the ref
itself:
// loadImage.js
watch(() => srcRef, /* callback */) ❌
watch(() => srcRef.value, /* callback */) ✅
watch(srcRef, /* callback */) ✅
The composable receives the image source in a ref
, and your onMounted()
hook is passing that ref
to loadImage()
, which is actually expecting the string in the ref
's unwrapped value:
// loadImage.js
onMounted(() => {
if (src) { ❌ /* src is a ref in this composable */
loadImage(src)
}
})
onMounted(() => {
if (src.value) { ✅
loadImage(src.value)
}
})
Upvotes: 8