--- /dev/null
+import {
+ computed,
+ getCurrentInstance,
+ h,
+ nodeOps,
+ render,
+} from '@vue/runtime-test'
+
+describe('api: computed', () => {
+ test('should warn if getCurrentInstance is called inside computed getter', () => {
+ const Comp = {
+ setup() {
+ const c = computed(() => {
+ getCurrentInstance()
+ return 1
+ })
+ return () => c.value
+ },
+ }
+ render(h(Comp), nodeOps.createElement('div'))
+ expect(
+ 'getCurrentInstance() called inside a computed getter',
+ ).toHaveBeenWarned()
+ })
+
+ test('should warn if getCurrentInstance is called inside computed getter (object syntax)', () => {
+ const Comp = {
+ setup() {
+ const c = computed({
+ get: () => {
+ getCurrentInstance()
+ return 1
+ },
+ set: () => {},
+ })
+ return () => c.value
+ },
+ }
+ render(h(Comp), nodeOps.createElement('div'))
+ expect(
+ 'getCurrentInstance() called inside a computed getter',
+ ).toHaveBeenWarned()
+ })
+})
-import { computed as _computed } from '@vue/reactivity'
+import {
+ type ComputedGetter,
+ type WritableComputedOptions,
+ computed as _computed,
+} from '@vue/reactivity'
import { isInSSRComponentSetup } from './component'
+import { isFunction } from '@vue/shared'
+
+/**
+ * For dev warning only.
+ * Context: https://github.com/vuejs/core/discussions/9974
+ */
+export let isInComputedGetter = false
+
+function wrapComputedGetter(
+ getter: ComputedGetter<unknown>,
+): ComputedGetter<unknown> {
+ return () => {
+ isInComputedGetter = true
+ try {
+ return getter()
+ } finally {
+ isInComputedGetter = false
+ }
+ }
+}
export const computed: typeof _computed = (
- getterOrOptions: any,
+ getterOrOptions: ComputedGetter<unknown> | WritableComputedOptions<unknown>,
debugOptions?: any,
) => {
- // @ts-expect-error
+ if (__DEV__) {
+ if (isFunction(getterOrOptions)) {
+ getterOrOptions = wrapComputedGetter(getterOrOptions)
+ } else {
+ getterOrOptions.get = wrapComputedGetter(getterOrOptions.get)
+ }
+ }
+
+ // @ts-expect-error the 3rd argument is hidden from public types
return _computed(getterOrOptions, debugOptions, isInSSRComponentSetup)
}
} from './compat/compatConfig'
import type { SchedulerJob } from './scheduler'
import type { LifecycleHooks } from './enums'
+import { isInComputedGetter } from './apiComputed'
export type Data = Record<string, unknown>
export let currentInstance: ComponentInternalInstance | null = null
-export const getCurrentInstance: () => ComponentInternalInstance | null = () =>
- currentInstance || currentRenderingInstance
+export const getCurrentInstance: () => ComponentInternalInstance | null =
+ () => {
+ if (__DEV__ && isInComputedGetter) {
+ warn(
+ `getCurrentInstance() called inside a computed getter. ` +
+ `This is incorrect usage as computed getters are not guaranteed ` +
+ `to be executed with an active component instance. If you are using ` +
+ `a composable inside a computed getter, move it ouside to the setup scope.`,
+ )
+ }
+ return currentInstance || currentRenderingInstance
+ }
let internalSetCurrentInstance: (
instance: ComponentInternalInstance | null,