From a2f20fcb039b6678398ee6a29fe10791875391c3 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Thu, 15 Sep 2022 19:52:16 +0200 Subject: [PATCH 1/3] feat(nuxt): `useData` composable --- packages/nuxt/src/app/composables/data.ts | 29 ++++++++++++++++++++++ packages/nuxt/src/app/composables/index.ts | 1 + packages/nuxt/src/imports/presets.ts | 1 + test/fixtures/basic/composables/random.ts | 7 ++++-- test/fixtures/basic/pages/random/[id].vue | 4 +-- 5 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 packages/nuxt/src/app/composables/data.ts diff --git a/packages/nuxt/src/app/composables/data.ts b/packages/nuxt/src/app/composables/data.ts new file mode 100644 index 00000000000..5c71a0210e9 --- /dev/null +++ b/packages/nuxt/src/app/composables/data.ts @@ -0,0 +1,29 @@ +import { toRef } from 'vue' +import type { Ref } from 'vue' +import { useNuxtApp } from '../nuxt' + +/** + * Create a global reactive ref that will be hydrated but not shared across ssr requests + * + * @param key a unique key ensuring that data fetching can be properly de-duplicated across requests + */ +export function useData (key: string): Ref { + if (!key || typeof key !== 'string') { + throw new TypeError('[nuxt] [useData] key must be a string: ' + key) + } + + const nuxt = useNuxtApp() + + // Ensure key is not conflicting with asyncData + // TODO: Can we actually share state with no overhead? + if (nuxt._asyncData[key]) { + throw new TypeError(`[nuxt] [useData] Key ${key} is already used by asyncData.`) + } + + if (!(key in nuxt.payload.data)) { + nuxt.payload.data[key] = undefined + } + const data = toRef(nuxt.payload.data, key) as Ref + + return data +} diff --git a/packages/nuxt/src/app/composables/index.ts b/packages/nuxt/src/app/composables/index.ts index d3af7370733..e07eca11cee 100644 --- a/packages/nuxt/src/app/composables/index.ts +++ b/packages/nuxt/src/app/composables/index.ts @@ -3,6 +3,7 @@ export { useAsyncData, useLazyAsyncData, refreshNuxtData } from './asyncData' export type { AsyncDataOptions, AsyncData } from './asyncData' export { useHydration } from './hydrate' export { useState } from './state' +export { useData } from './data' export { clearError, createError, isNuxtError, throwError, showError, useError } from './error' export type { NuxtError } from './error' export { useFetch, useLazyFetch } from './fetch' diff --git a/packages/nuxt/src/imports/presets.ts b/packages/nuxt/src/imports/presets.ts index f1d46bc6e98..dac3ee7e30d 100644 --- a/packages/nuxt/src/imports/presets.ts +++ b/packages/nuxt/src/imports/presets.ts @@ -30,6 +30,7 @@ const appPreset = defineUnimportPreset({ 'defineNuxtPlugin', 'useRuntimeConfig', 'useState', + 'useData', 'useFetch', 'useLazyFetch', 'useCookie', diff --git a/test/fixtures/basic/composables/random.ts b/test/fixtures/basic/composables/random.ts index 4e6114c6029..ceeda79790d 100644 --- a/test/fixtures/basic/composables/random.ts +++ b/test/fixtures/basic/composables/random.ts @@ -1,3 +1,6 @@ -export function useRandomState (max: number = 100, name = 'default') { - return useState('random:' + name, () => Math.round(Math.random() * max)) +export function useRandomData (max: number = 100, name = 'default') { + const data = useData('random:' + name) + if (!data.value) { + data.value = Math.floor(Math.random() * max) + } } diff --git a/test/fixtures/basic/pages/random/[id].vue b/test/fixtures/basic/pages/random/[id].vue index ba4d1db5f97..6fdb54202a2 100644 --- a/test/fixtures/basic/pages/random/[id].vue +++ b/test/fixtures/basic/pages/random/[id].vue @@ -40,8 +40,8 @@ const pageKey = 'rand_' + route.params.id const { data: randomNumbers, refresh } = await useFetch('/api/random', { key: pageKey as string }) -const random = useRandomState(100, pageKey) -const globalRandom = useRandomState(100) +const random = useRandomData(100, pageKey) +const globalRandom = useRandomData(100)