From 5c4a24f015baccbd51797d866a2b8c47521f804b Mon Sep 17 00:00:00 2001 From: Vadim Kruglov Date: Tue, 23 Apr 2024 23:56:37 +0700 Subject: [PATCH] fix(compiler-core): warn using reactive array as a ref inside v-for in script setup --- .../__tests__/rendererTemplateRef.spec.ts | 38 +++++++++++++++++++ .../runtime-core/src/rendererTemplateRef.ts | 16 +++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts b/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts index 9f26bf9bdf8..9196d9cf2d3 100644 --- a/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts +++ b/packages/runtime-core/__tests__/rendererTemplateRef.spec.ts @@ -539,4 +539,42 @@ describe('api: template refs', () => { '
[object Object],[object Object]
', ) }) + + // #10655 warn if reactive array used as a ref inside v-for + test('string ref pointing to reactive() in v-for, should warn that it will not work in prod for scripSetup', () => { + const list = ref([1, 2, 3]) + const reactiveRef = reactive([]) + const App = { + setup() { + return { + reactiveRef, + __isScriptSetup: true, + } + }, + render() { + return h('div', null, [ + h( + 'ul', + list.value.map(i => + h( + 'li', + { + ref: 'reactiveRef', + ref_for: true, + }, + i, + ), + ), + ), + ]) + }, + } + const root = nodeOps.createElement('div') + render(h(App), root) + + expect( + 'In production mode reactive ref array will not be filled. ' + + 'Use ref() instead.', + ).toHaveBeenWarned() + }) }) diff --git a/packages/runtime-core/src/rendererTemplateRef.ts b/packages/runtime-core/src/rendererTemplateRef.ts index b652edeac4c..1a33d1f1805 100644 --- a/packages/runtime-core/src/rendererTemplateRef.ts +++ b/packages/runtime-core/src/rendererTemplateRef.ts @@ -12,7 +12,7 @@ import { import { isAsyncWrapper } from './apiAsyncComponent' import { getExposeProxy } from './component' import { warn } from './warning' -import { isRef } from '@vue/reactivity' +import { isReactive, isRef } from '@vue/reactivity' import { ErrorCodes, callWithErrorHandling } from './errorHandling' import type { SchedulerJob } from './scheduler' import { queuePostRenderEffect } from './renderer' @@ -103,6 +103,20 @@ export function setRef( if (rawRef.k) refs[rawRef.k] = ref.value } } else if (!existing.includes(refValue)) { + // #10655 warn if reactive array used as a ref + if ( + __DEV__ && + _isString && + hasOwn(owner.devtoolsRawSetupState, ref) && + !isRef(owner.devtoolsRawSetupState[ref]) && + isReactive(owner.devtoolsRawSetupState[ref]) && + hasOwn(setupState, '__isScriptSetup') + ) { + warn( + 'In production mode reactive ref array will not be filled. ' + + 'Use ref() instead.', + ) + } existing.push(refValue) } }