Skip to content
This repository has been archived by the owner on Apr 6, 2023. It is now read-only.

feat(nuxt): useData composable #7569

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/nuxt/src/app/composables/data.ts
Original file line number Diff line number Diff line change
@@ -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 <T> (key: string): Ref<T|undefined> {
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<T|undefined>

return data
}
1 change: 1 addition & 0 deletions packages/nuxt/src/app/composables/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
1 change: 1 addition & 0 deletions packages/nuxt/src/imports/presets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const appPreset = defineUnimportPreset({
'defineNuxtPlugin',
'useRuntimeConfig',
'useState',
'useData',
'useFetch',
'useLazyFetch',
'useCookie',
Expand Down
2 changes: 1 addition & 1 deletion test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ describe.skipIf(process.env.NUXT_TEST_DEV || isWindows)('payload rendering', ()
it('renders a payload', async () => {
const payload = await $fetch('/random/a/_payload.js', { responseType: 'text' })
expect(payload).toMatch(
/export default \{data:\{\$frand_a:\[[^\]]*\]\},state:\{"\$srandom:rand_a":\d*,"\$srandom:default":\d*\},prerenderedAt:\d*\}/
/export default \{data:\{\$frand_a:\[[^\]]*\],"random:rand_a":\d*,"random:default":\d*\},state:\{},prerenderedAt:\d*\}/
)
})

Expand Down
7 changes: 5 additions & 2 deletions test/fixtures/basic/composables/random.ts
Original file line number Diff line number Diff line change
@@ -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)
}
}
4 changes: 2 additions & 2 deletions test/fixtures/basic/pages/random/[id].vue
Original file line number Diff line number Diff line change
Expand Up @@ -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)
</script>

<style scoped>
Expand Down