{"record":{"id":"a06616396afb6e43","repo":"jeecgboot/JeecgBoot","slug":"usemodal-can-only-be-used-inside-setup-or-func","errorCode":null,"errorMessage":"useModal() can only be used inside setup() or functional components!","messagePattern":"useModal\\(\\) can only be used inside setup\\(\\) or functional components!","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"jeecgboot-vue3/src/components/Modal/src/hooks/useModal.ts","lineNumber":24,"sourceCode":"import { tryOnUnmounted } from '@vueuse/core';\nimport { error } from '/@/utils/log';\nimport { computed } from 'vue';\n\nconst dataTransfer = reactive<any>({});\n\nconst visibleData = reactive<{ [key: number]: boolean }>({});\n\n/**\n * @description: Applicable to independent modal and call outside\n */\nexport function useModal(): UseModalReturnType {\n  const modal = ref<Nullable<ModalMethods>>(null);\n  const loaded = ref<Nullable<boolean>>(false);\n  const uid = ref<string>('');\n\n  function register(modalMethod: ModalMethods, uuid: string) {\n    if (!getCurrentInstance()) {\n      throw new Error('useModal() can only be used inside setup() or functional components!');\n    }\n    uid.value = uuid;\n    isProdMode() &&\n      onUnmounted(() => {\n        modal.value = null;\n        loaded.value = false;\n        dataTransfer[unref(uid)] = null;\n      });\n    if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return;\n\n    modal.value = modalMethod;\n    loaded.value = true;\n    modalMethod.emitVisible = (visible: boolean, uid: number) => {\n      visibleData[uid] = visible;\n    };\n  }\n\n  const getInstance = () => {","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/jeecgboot/JeecgBoot/blob/96fb33f5ec68516da0b0147da06b2eb0419e063a/jeecgboot-vue3/src/components/Modal/src/hooks/useModal.ts#L6-L42","documentation":"useModal creates a Modal controller via the register/methods pair pattern. Notably, the getCurrentInstance() guard here is placed inside the inner register() function rather than at the top of useModal() — so the throw happens when register() is invoked (i.e. when the child Modal component mounts and calls register with its instance) if that registration occurs outside a setup context. This differs from useDrawer/useDescription where the guard is at the hook entry. The guard protects the onUnmounted registration for cleanup of the dataTransfer map.","triggerScenarios":"The register() callback runs during the Modal component's setup; if that setup executes without an active instance (e.g. the Modal is rendered via a render function outside a component, or register is called imperatively), the guard throws. Also triggered in testing when the Modal component is mounted shallowly without proper instance wiring.","commonSituations":"Using <BasicModal> inside a custom render function that is not a component; mocking the Modal in tests; the Modal's v-bind:register being invoked through a context that lost the instance (rare); upgrading Vue versions that change getCurrentInstance behavior in certain edge cases.","solutions":["Ensure the component hosting <BasicModal> (the one that calls useModal and passes register) is a proper Vue component rendered in the tree.","Keep the [register, methods] = useModal() call and the template binding of register inside the same <script setup>.","In tests, use @vue/test-utils mount() rather than calling register manually.","Avoid calling register() imperatively in lifecycle hooks of a non-component context."],"exampleFix":"// before — register passed to a non-component render function\nexport const openMyModal = () => {\n  const [register, { openModal }] = useModal();\n  register(modalInstance, uuid); // throws inside register\n};\n\n// after — register bound in a real component template\n<template><BasicModal @register=\"register\" /></template>\n<script setup>const [register, { openModal }] = useModal();</script>","handlingStrategy":"validation","validationCode":"import { getCurrentInstance } from 'vue';\nfunction registerSafely(register, modalMethod, uuid) {\n  if (!getCurrentInstance()) {\n    console.warn('register() called outside setup; ensure BasicModal is in a real component');\n    return;\n  }\n  register(modalMethod, uuid);\n}","typeGuard":"import { getCurrentInstance } from 'vue';\nconst inSetup = () => getCurrentInstance() != null;","tryCatchPattern":"try {\n  register(modalMethod, uuid);\n} catch (e) {\n  // the host component is not a proper Vue component; wrap in defineComponent\n}","preventionTips":["Always bind register via the template (@register) of a real <BasicModal>.","In tests use mount() from @vue/test-utils rather than manual register calls.","Avoid imperative register() calls in non-component code."],"tags":["vue3","composition-api","modal","setup","lifecycle"],"backgroundTag":null,"analyzedSha":"96fb33f5ec68516da0b0147da06b2eb0419e063a","analyzedAt":"2026-08-14T00:04:16.786Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}