jeecgboot/JeecgBoot · error
useModal() can only be used inside setup() or functional com
Error message
useModal() can only be used inside setup() or functional components!
What it means
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.
Source
Thrown at jeecgboot-vue3/src/components/Modal/src/hooks/useModal.ts:24
import { tryOnUnmounted } from '@vueuse/core';
import { error } from '/@/utils/log';
import { computed } from 'vue';
const dataTransfer = reactive<any>({});
const visibleData = reactive<{ [key: number]: boolean }>({});
/**
* @description: Applicable to independent modal and call outside
*/
export function useModal(): UseModalReturnType {
const modal = ref<Nullable<ModalMethods>>(null);
const loaded = ref<Nullable<boolean>>(false);
const uid = ref<string>('');
function register(modalMethod: ModalMethods, uuid: string) {
if (!getCurrentInstance()) {
throw new Error('useModal() can only be used inside setup() or functional components!');
}
uid.value = uuid;
isProdMode() &&
onUnmounted(() => {
modal.value = null;
loaded.value = false;
dataTransfer[unref(uid)] = null;
});
if (unref(loaded) && isProdMode() && modalMethod === unref(modal)) return;
modal.value = modalMethod;
loaded.value = true;
modalMethod.emitVisible = (visible: boolean, uid: number) => {
visibleData[uid] = visible;
};
}
const getInstance = () => {View on GitHub (pinned to 96fb33f5ec)
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.
Example fix
// before — register passed to a non-component render function
export const openMyModal = () => {
const [register, { openModal }] = useModal();
register(modalInstance, uuid); // throws inside register
};
// after — register bound in a real component template
<template><BasicModal @register="register" /></template>
<script setup>const [register, { openModal }] = useModal();</script> Defensive patterns
Strategy: validation
Validate before calling
import { getCurrentInstance } from 'vue';
function registerSafely(register, modalMethod, uuid) {
if (!getCurrentInstance()) {
console.warn('register() called outside setup; ensure BasicModal is in a real component');
return;
}
register(modalMethod, uuid);
} Type guard
import { getCurrentInstance } from 'vue';
const inSetup = () => getCurrentInstance() != null; Try / catch
try {
register(modalMethod, uuid);
} catch (e) {
// the host component is not a proper Vue component; wrap in defineComponent
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- useDescription() can only be used inside setup() or function
- useDrawer() can only be used inside setup() or functional co
- useDrawerInner() can only be used inside setup() or function
- useTableContext must be used after createTableContext
- 提交任务失败
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/a06616396afb6e43.
Report an issue: GitHub.