From d9cf95fdb8359a783a4dd06fe4c83152acf71e7a Mon Sep 17 00:00:00 2001 From: Ohb00 <43827372+OhB00@users.noreply.github.com> Date: Tue, 30 Aug 2022 11:31:12 +0200 Subject: [PATCH 1/2] test: add tests for async-data shared state --- test/basic.test.ts | 18 +++++++++ .../basic/composables/asyncDataTests.ts | 7 ++++ .../basic/pages/useAsyncData/double.vue | 26 +++++++++++++ .../basic/pages/useAsyncData/promise-all.vue | 31 +++++++++++++++ .../basic/pages/useAsyncData/refresh.vue | 39 +++++++++++++++++++ .../basic/pages/useAsyncData/single.vue | 16 ++++++++ .../basic/server/api/useAsyncData/count.ts | 3 ++ 7 files changed, 140 insertions(+) create mode 100644 test/fixtures/basic/composables/asyncDataTests.ts create mode 100644 test/fixtures/basic/pages/useAsyncData/double.vue create mode 100644 test/fixtures/basic/pages/useAsyncData/promise-all.vue create mode 100644 test/fixtures/basic/pages/useAsyncData/refresh.vue create mode 100644 test/fixtures/basic/pages/useAsyncData/single.vue create mode 100644 test/fixtures/basic/server/api/useAsyncData/count.ts diff --git a/test/basic.test.ts b/test/basic.test.ts index f9574c562d8..f41310a14a5 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -527,3 +527,21 @@ describe('app config', () => { expect(html).toContain(JSON.stringify(expectedAppConfig)) }) }) + +describe('useAsyncData', () => { + it('single request resolves', async () => { + await expectNoClientErrors('/useAsyncData/single') + }) + + it('two requests resolve', async () => { + await expectNoClientErrors('/useAsyncData/double') + }) + + it('two requests resolve and sync', async () => { + await $fetch('/useAsyncData/refresh') + }) + + it('two requests made at once resolve and sync', async () => { + await expectNoClientErrors('/useAsyncData/promise-all') + }) +}) diff --git a/test/fixtures/basic/composables/asyncDataTests.ts b/test/fixtures/basic/composables/asyncDataTests.ts new file mode 100644 index 00000000000..07dd078ae87 --- /dev/null +++ b/test/fixtures/basic/composables/asyncDataTests.ts @@ -0,0 +1,7 @@ +export const useSleep = () => useAsyncData('sleep', async () => { + await new Promise(resolve => setTimeout(resolve, 50)) + + return 'Slept!' +}) + +export const useCounter = () => useFetch('/api/useAsyncData/count') diff --git a/test/fixtures/basic/pages/useAsyncData/double.vue b/test/fixtures/basic/pages/useAsyncData/double.vue new file mode 100644 index 00000000000..2c7c3a86cd8 --- /dev/null +++ b/test/fixtures/basic/pages/useAsyncData/double.vue @@ -0,0 +1,26 @@ + + + diff --git a/test/fixtures/basic/pages/useAsyncData/promise-all.vue b/test/fixtures/basic/pages/useAsyncData/promise-all.vue new file mode 100644 index 00000000000..426ebba9519 --- /dev/null +++ b/test/fixtures/basic/pages/useAsyncData/promise-all.vue @@ -0,0 +1,31 @@ + + + diff --git a/test/fixtures/basic/pages/useAsyncData/refresh.vue b/test/fixtures/basic/pages/useAsyncData/refresh.vue new file mode 100644 index 00000000000..5f312fd434c --- /dev/null +++ b/test/fixtures/basic/pages/useAsyncData/refresh.vue @@ -0,0 +1,39 @@ + + + diff --git a/test/fixtures/basic/pages/useAsyncData/single.vue b/test/fixtures/basic/pages/useAsyncData/single.vue new file mode 100644 index 00000000000..b4dd2758880 --- /dev/null +++ b/test/fixtures/basic/pages/useAsyncData/single.vue @@ -0,0 +1,16 @@ + + + diff --git a/test/fixtures/basic/server/api/useAsyncData/count.ts b/test/fixtures/basic/server/api/useAsyncData/count.ts new file mode 100644 index 00000000000..8c33e3545d9 --- /dev/null +++ b/test/fixtures/basic/server/api/useAsyncData/count.ts @@ -0,0 +1,3 @@ +let counter = 0 + +export default () => ({ count: counter++ }) From 5b36d48a389d918bb87a8fd518b52a923c4f0989 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Tue, 30 Aug 2022 11:55:22 +0200 Subject: [PATCH 2/2] fix(nuxt): use shared state for `useAsyncData` --- packages/nuxt/src/app/composables/asyncData.ts | 15 ++++++++++----- packages/nuxt/src/app/nuxt.ts | 8 +++++++- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/packages/nuxt/src/app/composables/asyncData.ts b/packages/nuxt/src/app/composables/asyncData.ts index 7318718becf..a7596859e43 100644 --- a/packages/nuxt/src/app/composables/asyncData.ts +++ b/packages/nuxt/src/app/composables/asyncData.ts @@ -114,11 +114,16 @@ export function useAsyncData< const useInitialCache = () => (nuxt.isHydrating || options.initialCache) && nuxt.payload.data[key] !== undefined - const asyncData = { - data: ref(useInitialCache() ? nuxt.payload.data[key] : options.default?.() ?? null), - pending: ref(!useInitialCache()), - error: ref(nuxt.payload._errors[key] ?? null) - } as AsyncData + // Create or use a shared asyncData entity + if (!nuxt._asyncData[key]) { + nuxt._asyncData[key] = { + data: ref(useInitialCache() ? nuxt.payload.data[key] : options.default?.() ?? null), + pending: ref(!useInitialCache()), + error: ref(nuxt.payload._errors[key] ?? null) + } + } + // TODO: Else, Soemhow check for confliciting keys with different defaults or fetcher + const asyncData = { ...nuxt._asyncData[key] } as AsyncData asyncData.refresh = (opts = {}) => { // Avoid fetching same key more than once at a time diff --git a/packages/nuxt/src/app/nuxt.ts b/packages/nuxt/src/app/nuxt.ts index 812022729dc..cdada80b45a 100644 --- a/packages/nuxt/src/app/nuxt.ts +++ b/packages/nuxt/src/app/nuxt.ts @@ -1,5 +1,5 @@ /* eslint-disable no-use-before-define */ -import { getCurrentInstance, reactive } from 'vue' +import { getCurrentInstance, reactive, Ref } from 'vue' import type { App, onErrorCaptured, VNode } from 'vue' import { createHooks, Hookable } from 'hookable' import type { RuntimeConfig, AppConfigInput } from '@nuxt/schema' @@ -66,6 +66,11 @@ interface _NuxtApp { [key: string]: any _asyncDataPromises: Record | undefined> + _asyncData: Record + pending: Ref + error: Ref + }>, ssrContext?: NuxtSSRContext payload: { @@ -113,6 +118,7 @@ export function createNuxtApp (options: CreateOptions) { }), isHydrating: process.client, _asyncDataPromises: {}, + _asyncData: {}, ...options } as any as NuxtApp