Vue3中useFetch获取的数据无法作为数组循环的问题
问题描述
我使用Vuejs.org提供的组合式函数useFetch:
// fetch.js import { ref, isRef, unref, watchEffect } from 'vue' export function useFetch(url) { const data = ref(null) const error = ref(null) function doFetch() { // reset state before fetching.. data.value = null error.value = null // unref() unwraps potential refs fetch(unref(url)) .then((res) => res.json()) .then((json) => (data.value = json)) .catch((err) => (error.value = err)) } if (isRef(url)) { // setup reactive re-fetch if input URL is a ref watchEffect(doFetch) } else { // otherwise, just fetch once // and avoid the overhead of a watcher doFetch() } return { data, error } }
在script标签中这样使用:
let loopableValues = ref([]) const { data, error } = useFetch( 'https://jsonplaceholder.typicode.com/todos/', ) loopableValues.value = data return { loopableValues }
尝试在模板中循环时抛出错误:
<div v-for="value in loopableValues" :key="value.id"> {{ value.id}}</div>
错误信息:Uncaught (in promise) TypeError: value is undefined
我认为需要处理Promise,但不知道具体该怎么做,是要对useFetch返回的数据做处理,还是遗漏了其他步骤?
解决方案
问题出在两个核心点:
- 错误赋值ref对象:
loopableValues.value = data把data这个ref对象直接赋值给了loopableValues的value,导致loopableValues实际指向的是一个ref容器而非数组数据,循环时自然无法读取到数组项。 - 初始状态未处理:
useFetch中data初始值为null,请求完成前模板循环null会触发value is undefined错误。
修正方案一:直接使用返回的data并处理初始状态
const { data, error } = useFetch('https://jsonplaceholder.typicode.com/todos/') return { data, error }
模板中添加空值兜底:
<div v-for="value in data || []" :key="value.id"> {{ value.id}}</div>
修正方案二:正确赋值loopableValues
如果必须使用loopableValues,通过监听data的变化来同步数据:
import { ref, watch } from 'vue' // ... const loopableValues = ref([]) const { data, error } = useFetch('https://jsonplaceholder.typicode.com/todos/') // 监听data变化,同步到loopableValues watch(data, (newVal) => { loopableValues.value = newVal || [] }, { immediate: true }) return { loopableValues, error }
模板无需额外修改:
<div v-for="value in loopableValues" :key="value.id"> {{ value.id}}</div>
可选优化:给useFetch添加加载状态
为了提升用户体验,可以给useFetch扩展加载状态,方便在模板中处理加载中、出错场景:
// fetch.js import { ref, isRef, unref, watchEffect } from 'vue' export function useFetch(url) { const data = ref(null) const error = ref(null) const loading = ref(false) // 新增加载状态 function doFetch() { data.value = null error.value = null loading.value = true // 标记加载中 fetch(unref(url)) .then((res) => res.json()) .then((json) => (data.value = json)) .catch((err) => (error.value = err)) .finally(() => loading.value = false) // 加载结束 } if (isRef(url)) { watchEffect(doFetch) } else { doFetch() } return { data, error, loading } }
使用时的模板可以更友好:
<div v-if="loading">加载中...</div> <div v-else-if="error">请求出错:{{ error.message }}</div> <div v-else v-for="value in data || []" :key="value.id"> {{ value.id}}</div>
内容的提问来源于stack exchange,提问作者DavidLar
相关产品推荐
相关产品推荐

