feat: useAsyncData (#142)

* feat: useAsyncData

Resolves #141

* chore: improvements

* chore: fix lint
This commit is contained in:
Sébastien Chopin 2021-02-03 19:14:30 +01:00 committed by GitHub
parent b04e212ebc
commit a8707469f8
3 changed files with 31 additions and 29 deletions

View File

@ -1,25 +1,22 @@
import { Ref, toRef, onMounted, watch, getCurrentInstance, onUnmounted } from 'vue' import { Ref, ref, toRef, onMounted, watch, getCurrentInstance, onUnmounted } from 'vue'
import { Nuxt, useNuxt } from 'nuxt/app' import { Nuxt, useNuxt } from 'nuxt/app'
import { $fetch } from 'ohmyfetch'
import { useData } from './data' import { useData } from './data'
export type HTTPRequest = string | { method: string, url: string } export type AsyncDataFn<T> = (ctx?: Nuxt) => Promise<T>
export type FetchRequest<T> = HTTPRequest | ((ctx: Nuxt) => HTTPRequest | Promise<T>)
export interface FetchOptions { export interface AsyncDataOptions {
server?: boolean server?: boolean
defer?: boolean defer?: boolean
fetcher?: Function
key?: string
} }
export interface FetchObj<T> { export interface AsyncDataObj<T> {
data: Ref<T> data: Ref<T>
fetch: Function pending: Ref<boolean>
refresh: Function
error?: any error?: any
} }
export function useFetch (defaults?: FetchOptions) { export function useAsyncData (defaults?: AsyncDataOptions) {
const nuxt = useNuxt() const nuxt = useNuxt()
const vm = getCurrentInstance() const vm = getCurrentInstance()
@ -40,31 +37,32 @@ export function useFetch (defaults?: FetchOptions) {
}) })
} }
return async function fetch<T = any> (request: FetchRequest<T>, options?: FetchOptions): Promise<FetchObj<T>> { return async function asyncData<T = any> (handler: AsyncDataFn<T>, options?: AsyncDataOptions): Promise<AsyncDataObj<T>> {
if (typeof handler !== 'function') {
throw new TypeError('asyncData handler must be a function')
}
options = { options = {
server: true, server: true,
defer: false, defer: false,
fetcher: globalThis.$fetch || $fetch,
...defaults, ...defaults,
...options ...options
} }
const key = String(dataRef++) const key = String(dataRef++)
const pending = ref(true)
const fetch = async () => { const fetch = async () => {
const _request = typeof request === 'function' pending.value = true
? request(nuxt) const _handler = handler(nuxt)
: request
if (_request instanceof Promise) { if (_handler instanceof Promise) {
// Let user resolve if request is promise // Let user resolve if request is promise
data[key] = await _request // TODO: handle error
} else if (_request && (typeof _request === 'string' || _request.url)) { data[key] = await _handler
// Make HTTP request when request is string (url) or { url, ...opts } pending.value = false
data[key] = await options.fetcher(_request)
} else { } else {
// Invalid request // Invalid request
throw new Error('Invalid fetch request: ' + _request) throw new TypeError('Invalid asyncData handler: ' + _handler)
} }
} }
@ -73,6 +71,9 @@ export function useFetch (defaults?: FetchOptions) {
// Client side // Client side
if (process.client) { if (process.client) {
// 1. Hydration (server: true): no fetch // 1. Hydration (server: true): no fetch
if (nuxt.isHydrating && options.server) {
pending.value = false
}
// 2. Initial load (server: false): fetch on mounted // 2. Initial load (server: false): fetch on mounted
if (nuxt.isHydrating && !options.server) { if (nuxt.isHydrating && !options.server) {
// Fetch on mounted (initial load or deferred fetch) // Fetch on mounted (initial load or deferred fetch)
@ -86,10 +87,8 @@ export function useFetch (defaults?: FetchOptions) {
await fetch() await fetch()
} }
} }
// Watch request // Watch handler
if (typeof request === 'function') { watch(handler.bind(null, nuxt), fetch)
watch(request, fetch)
}
} }
// Server side // Server side
@ -99,7 +98,8 @@ export function useFetch (defaults?: FetchOptions) {
return { return {
data: toRef<any, string>(data, key), data: toRef<any, string>(data, key),
fetch pending,
refresh: fetch
} }
} }
} }

View File

@ -1,3 +1,3 @@
export { useFetch } from './fetch' export { useAsyncData } from './asyncData'
export { useData } from './data' export { useData } from './data'
export { useHydration } from './hydrate' export { useHydration } from './hydrate'

View File

@ -51,11 +51,13 @@ export function createNuxt (options: CreateOptions) {
nuxt.provide = (name: string, value: any) => { nuxt.provide = (name: string, value: any) => {
const $name = '$' + name const $name = '$' + name
defineGetter(nuxt.app, $name, value) defineGetter(nuxt, $name, value)
defineGetter(nuxt.app.config.globalProperties, $name, value) defineGetter(nuxt.app.config.globalProperties, $name, value)
} }
nuxt.provide('nuxt', nuxt) // Inject $nuxt
defineGetter(nuxt.app, '$nuxt', nuxt)
defineGetter(nuxt.app.config.globalProperties, '$nuxt', nuxt)
// Expose nuxt to the renderContext // Expose nuxt to the renderContext
if (nuxt.ssrContext) { if (nuxt.ssrContext) {