如何正确包装 `useFetch` 来访问反应性?

问题描述 投票:0回答:1

我将

useFetch()
包装为可组合项,以提供自定义基本 URL 并自动设置身份验证令牌,但是当我在不带
await
关键字的组件中调用可组合项时,它的行为不会按预期响应。通常,我可以调用 const
{ data, pending, status } = useFetch('...')
,一旦 Promise 解决且
data
可用,UI 就会更新,但对于包装器来说这是行不通的。看来我必须始终将
await
useCustomFetch()

一起使用

知道为什么这行不通吗?

下面是自定义包装。

import type { UseFetchOptions } from 'nuxt/app'
import { defu } from 'defu'

export async function useCustomFetch<T>(url: string, options: UseFetchOptions<T> = {}) {
  const config = useRuntimeConfig()
  const { data, status, getSession }: { data: any; status: any; getSession: Function } = useAuth()

  const headers: HeadersInit | undefined = {}

  // NOTE: Set 'Authorization' header
  if (data && status.value === 'authenticated') {
    const session = await getSession({ required: true })
    headers.Authorization = `Bearer ${session.accessToken}`
  }

  const defaults: UseFetchOptions<T> = {
    baseURL: config.public.baseApiUrl,
    headers,
    onResponse(_ctx) {
      // EX: _ctx.response._data = new myBusinessResponse(_ctx.response._data)
    },
    onResponseError(_ctx) {
      // TODO: Send notification to Sentry
    },
  }

  // NOTE: Deep defaults, use unjs/defu
  const params = defu(options, defaults)

  return useFetch(url, params)
}

在组件中,这不起作用

<script setup>
const { data, pending, status } = useCustomFetch('/resources')
</script>
typescript vue.js nuxt.js vuejs3 nuxt3
1个回答
0
投票

useAsyncData
和其他依赖它的可组合项(在本例中为
useFetch
)返回增强的承诺。
async
函数始终返回本机 Promise,不会影响其工作方式,但没有来自增强 Promise 的
data
等。为了保留原始行为,
useCustomFetch
应该使用原始承诺并实现增强。

function useCustomFetch<T>(url: string, options: UseFetchOptions<T> = {}) {
  let promise = Promise.resolve<unknown>(null);

  if (data && status.value === 'authenticated') {
    promise = promise
      .then(() => getSession({ required: true }))
      .then(session => {
        headers.Authorization = `Bearer ${session.accessToken}`
      })
  }

  ...

  promise = promise
    .then(() => useFetch(url, params));
  Object.assign(promise, ???);

  return promise as ReturnType<(typeof useFetch)<T>;
}

可以看出这种方法会产生问题,因为当结果立即返回时不存在要扩充的属性。这将需要通过创建新的

data
等参考并将其结果同步到
useFetch
的结果来增加另一个级别的复杂性。
await getSession
使实现变得不必要的复杂,并且在没有
await
的情况下使用可组合项时可能会导致不正确的行为。

getSession
调用可能应该移至 fetch 选项,然后这变得简单,这允许推断结果的类型:

function useCustomFetch<T>(url: string, options: UseFetchOptions<T> = {}) {
  ...
 const defaults: UseFetchOptions<T> = {
   async onRequest(_ctx) {
     const session = await getSession({ required: true })
     _ctx.options = {
       ..._ctx.options.headers,
       _ctx.options.Authorization: `Bearer ${session.accessToken}`
     }
   },
  ...
  return useFetch(url, params);
}

请注意,

文档
中未列出增强承诺和不带 await 的用法。由于这没有记录,因此不应依赖它,并且可能会更改,恕不另行通知。

如果请求应该延迟执行而不等待结果,

useFetch
,因此自定义可组合项可以与
lazy
选项一起使用:

await useCustomFetch({ lazy: true, ... })
© www.soinside.com 2019 - 2024. All rights reserved.