diff --git a/docs/content/en/helpers/reqRef.md b/docs/content/en/helpers/reqRef.md
index e837bf13..f20d78c0 100644
--- a/docs/content/en/helpers/reqRef.md
+++ b/docs/content/en/helpers/reqRef.md
@@ -3,13 +3,16 @@ title: reqRef, reqSsrRef
description: '@nuxtjs/composition-api provides a way to use the Vue 3 Composition API with Nuxt-specific features.'
category: Helpers
fullscreen: True
+badge: deprecated
version: 0.133
position: 12
---
`reqRef` declares a normal `ref` with one key difference. It resets the value of this ref on each request. You can find out [more information here](/getting-started/gotchas#shared-server-state).
-You should take especial care because of the danger of shared state when using refs in this way.
+You do not need a `reqRef` if you are using an `ssrRef` within a component setup function as it will be automatically tied to the per-request state.
+
+You should take especial care because of the danger of shared state when using refs in this way.
## Example
diff --git a/docs/content/en/helpers/ssrRef.md b/docs/content/en/helpers/ssrRef.md
index b1f05791..b5c929d7 100644
--- a/docs/content/en/helpers/ssrRef.md
+++ b/docs/content/en/helpers/ssrRef.md
@@ -8,6 +8,8 @@ position: 14
When creating composition utility functions, often there will be server-side state that needs to be conveyed to the client.
+If initialised within `setup()` or via `onGlobalSetup`, `ssrRef` data will exist *only* within the request state. If initialised *outside* a component there is the possibility that an `ssrRef` may share state across requests.
+
## ssrRef
`ssrRef` will automatically add ref values to `window.__NUXT__` on SSR if they have been changed from their initial value. It can be used outside of components, such as in shared utility functions, and it supports passing a factory function that will generate the initial value of the ref.
diff --git a/src/hooks.ts b/src/hooks.ts
index deefd19a..0406f2af 100644
--- a/src/hooks.ts
+++ b/src/hooks.ts
@@ -44,12 +44,14 @@ export const setMetaPlugin: Plugin = context => {
* @private
*/
export const globalPlugin: Plugin = context => {
- const { setup } = context.app
- globalSetup = new Set()
if (process.server) {
reqRefs.forEach(reset => reset())
+ setSSRContext(context.app)
}
+ const { setup } = context.app
+ globalSetup = new Set()
+
context.app.setup = function (...args) {
let result = {}
if (setup instanceof Function) {
@@ -60,9 +62,4 @@ export const globalPlugin: Plugin = context => {
}
return result
}
-
- if (!process.server) return
- if (context.app.context.ssrContext) {
- setSSRContext(context.app.context.ssrContext)
- }
}
diff --git a/src/req-ref.ts b/src/req-ref.ts
index b5ada4f4..2d69752d 100644
--- a/src/req-ref.ts
+++ b/src/req-ref.ts
@@ -4,6 +4,9 @@ import { sanitise, ssrRef } from './ssr-ref'
export const reqRefs = new Set<() => void>()
+/**
+ * @deprecated
+ */
export const reqRef = (initialValue: T): Ref => {
const _ref = ref(initialValue)
@@ -12,6 +15,9 @@ export const reqRef = (initialValue: T): Ref => {
return _ref as Ref
}
+/**
+ * @deprecated
+ */
export const reqSsrRef = (initialValue: T, key?: string) => {
const _ref = ssrRef(initialValue, key)
diff --git a/src/ssr-ref.ts b/src/ssr-ref.ts
index 455db019..84273c62 100644
--- a/src/ssr-ref.ts
+++ b/src/ssr-ref.ts
@@ -1,5 +1,6 @@
import {
customRef,
+ getCurrentInstance,
onServerPrefetch,
ref,
shallowRef,
@@ -14,11 +15,39 @@ function getValue(value: T | (() => T)): T {
return value
}
-let data: any = {}
+let globalRefs: any = {}
-export function setSSRContext(ssrContext: any) {
- data = Object.assign({}, {})
- ssrContext.nuxt.ssrRefs = data
+export function setSSRContext(app: any) {
+ globalRefs = Object.assign({}, {})
+ app.context.ssrContext.nuxt.globalRefs = globalRefs
+}
+
+const useServerData = () => {
+ let type: 'globalRefs' | 'ssrRefs' = 'globalRefs'
+
+ const vm = getCurrentInstance()
+
+ if (vm) {
+ type = 'ssrRefs'
+ if (process.server) {
+ const { ssrContext } = vm[globalNuxt].context
+ ;(ssrContext as any).nuxt.ssrRefs = (ssrContext as any).nuxt.ssrRefs || {}
+ }
+ }
+
+ const setData = (key: string, val: any) => {
+ switch (type) {
+ case 'globalRefs':
+ globalRefs[key] = sanitise(val)
+ break
+ case 'ssrRefs':
+ ;(vm![globalNuxt].context.ssrContext as any).nuxt.ssrRefs[
+ key
+ ] = sanitise(val)
+ }
+ }
+
+ return { type, setData }
}
const isProxyable = (val: unknown): val is Record =>
@@ -27,7 +56,11 @@ const isProxyable = (val: unknown): val is Record =>
export const sanitise = (val: unknown) =>
(val && JSON.parse(JSON.stringify(val))) || val
-const ssrValue = (value: T | (() => T), key: string): T => {
+const ssrValue = (
+ value: T | (() => T),
+ key: string,
+ type: 'globalRefs' | 'ssrRefs' = 'globalRefs'
+): T => {
if (process.client) {
if (
process.env.NODE_ENV === 'development' &&
@@ -35,7 +68,7 @@ const ssrValue = (value: T | (() => T), key: string): T => {
) {
return getValue(value)
}
- return (window as any)[globalContext]?.ssrRefs?.[key] ?? getValue(value)
+ return (window as any)[globalContext]?.[type]?.[key] ?? getValue(value)
}
return getValue(value)
}
@@ -61,11 +94,14 @@ const ssrValue = (value: T | (() => T), key: string): T => {
*/
export const ssrRef = (value: T | (() => T), key?: string): Ref => {
validateKey(key)
- let val = ssrValue(value, key)
+
+ const { type, setData } = useServerData()
+
+ let val = ssrValue(value, key, type)
if (process.client) return ref(val) as Ref
- if (value instanceof Function) data[key] = sanitise(val)
+ if (value instanceof Function) setData(key, val)
const getProxy = >(
track: () => void,
@@ -81,7 +117,7 @@ export const ssrRef = (value: T | (() => T), key?: string): Ref => {
},
set(obj, prop, newVal) {
const result = Reflect.set(obj, prop, newVal)
- data[key] = sanitise(val)
+ setData(key, val)
trigger()
return result
},
@@ -94,7 +130,7 @@ export const ssrRef = (value: T | (() => T), key?: string): Ref => {
return val
},
set: (v: T) => {
- data[key] = sanitise(v)
+ setData(key, v)
val = v
trigger()
},
@@ -107,7 +143,7 @@ export const ssrRef = (value: T | (() => T), key?: string): Ref => {
* This helper creates a [`shallowRef`](https://vue-composition-api-rfc.netlify.app/api.html#shallowref) (a ref that tracks its own .value mutation but doesn't make its value reactive) that is synced between client & server.
* @param value This can be an initial value or a factory function that will be executed on server-side to get the initial value.
* @param key Under the hood, `shallowSsrRef` requires a key to ensure that the ref values match between client and server. If you have added `@nuxtjs/composition-api` to your `buildModules`, this will be done automagically by an injected Babel plugin. If you need to do things differently, you can specify a key manually or add `@nuxtjs/composition-api/babel` to your Babel plugins.
-
+
* @example
```ts
import { shallowSsrRef, onMounted } from '@nuxtjs/composition-api'
@@ -127,13 +163,14 @@ export const shallowSsrRef = (
key?: string
): Ref => {
validateKey(key)
+ const { type, setData } = useServerData()
- if (process.client) return shallowRef(ssrValue(value, key))
+ if (process.client) return shallowRef(ssrValue(value, key, type))
const _val = getValue(value)
if (value instanceof Function) {
- data[key] = sanitise(_val)
+ setData(key, _val)
}
return customRef((track, trigger) => ({
@@ -142,7 +179,7 @@ export const shallowSsrRef = (
return _val
},
set(newValue: T) {
- data[key] = sanitise(newValue)
+ setData(key, newValue)
value = newValue
trigger()
},
@@ -167,13 +204,13 @@ export const shallowSsrRef = (
setup() {
const _promise = ssrPromise(async () => myAsyncFunction())
const resolvedPromise = ref(null)
-
+
onBeforeMount(async () => {
resolvedPromise.value = await _promise
})
return {
- // On the server, this will be null until the promise resolves.
+ // On the server, this will be null until the promise resolves.
// On the client, if server-rendered, this will always be the resolved promise.
resolvedPromise,
}
@@ -186,12 +223,13 @@ export const ssrPromise = (
key?: string
): Promise => {
validateKey(key)
+ const { type, setData } = useServerData()
- const val = ssrValue(value, key)
+ const val = ssrValue(value, key, type)
if (process.client) return Promise.resolve(val)
onServerPrefetch(async () => {
- data[key] = sanitise(await val)
+ setData(key, await val)
})
return val
}
diff --git a/test/unit/__snapshots__/ssr-ref.spec.ts.snap b/test/unit/__snapshots__/ssr-ref.spec.ts.snap
index 9e9089b3..34af58c0 100644
--- a/test/unit/__snapshots__/ssr-ref.spec.ts.snap
+++ b/test/unit/__snapshots__/ssr-ref.spec.ts.snap
@@ -3,6 +3,7 @@
exports[`ssrRef reactivity ssrRefs react to change in state 1`] = `
Object {
"nuxt": Object {
+ "globalRefs": Object {},
"ssrRefs": Object {
"name": "full name",
},
@@ -13,6 +14,7 @@ Object {
exports[`ssrRef reactivity ssrRefs react to deep change in array state 1`] = `
Object {
"nuxt": Object {
+ "globalRefs": Object {},
"ssrRefs": Object {
"obj": Object {
"deep": Object {
@@ -31,6 +33,7 @@ Object {
exports[`ssrRef reactivity ssrRefs react to deep change in object state 1`] = `
Object {
"nuxt": Object {
+ "globalRefs": Object {},
"ssrRefs": Object {
"obj": Object {
"deep": Object {
@@ -43,3 +46,14 @@ Object {
},
}
`;
+
+exports[`ssrRef reactivity ssrRefs within multiple requests 1`] = `
+Object {
+ "nuxt": Object {
+ "globalRefs": Object {},
+ "ssrRefs": Object {
+ "concurrentRef": 2,
+ },
+ },
+}
+`;
diff --git a/test/unit/ssr-ref.spec.ts b/test/unit/ssr-ref.spec.ts
index 7141e98a..35dbbfab 100644
--- a/test/unit/ssr-ref.spec.ts
+++ b/test/unit/ssr-ref.spec.ts
@@ -1,14 +1,13 @@
/**
* @jest-environment jsdom
*/
-import { ssrRef, setSSRContext } from '../..'
+import { ssrRef, globalPlugin } from '../..'
+import * as cAPI from '@vue/composition-api'
jest.setTimeout(60000)
-/* eslint-disable @typescript-eslint/no-var-requires */
-const { setup, get } = require('@nuxtjs/module-test-utils')
-const config = require('../fixture/nuxt.config')
-/* eslint-enable */
+import { setup, get } from '@nuxtjs/module-test-utils'
+import config from '../fixture/nuxt.config'
let nuxt
@@ -45,8 +44,14 @@ describe('ssrRef reactivity', () => {
let ssrContext: Record
beforeEach(async () => {
+ process.server = true
ssrContext = Object.assign({}, { nuxt: {} })
- setSSRContext(ssrContext)
+ ;(cAPI as any).getCurrentInstance = () => ({
+ $nuxt: {
+ context: { ssrContext },
+ },
+ })
+ globalPlugin({ app: { context: { ssrContext } } } as any, null)
})
test('ssrRefs react to change in state', async () => {
process.client = false
@@ -67,4 +72,18 @@ describe('ssrRef reactivity', () => {
obj.value.deep.object[0].name = 'full name'
expect(ssrContext).toMatchSnapshot()
})
+
+ test('ssrRefs within multiple requests', async () => {
+ const concurrentRef = ssrRef(1, 'concurrentRef')
+
+ // simulate the new request comes in
+ globalPlugin(
+ { app: { context: { ssrContext: { nuxt: {} } } } } as any,
+ null
+ )
+
+ concurrentRef.value = 2
+
+ expect(ssrContext).toMatchSnapshot()
+ })
})