Home > Software design >  [Vue warn]: inject() can only be used inside setup() or functional components
[Vue warn]: inject() can only be used inside setup() or functional components

Time:05-30

I wanted to use the Toast component from PrimeVue library and I also wanted to create a nice re-usable service for it but I am getting this error. This doesn't seem to be a problem if I don't try to extract a separate service for the Toast notification.

But I do want to call useToast() from inside my custom service and not directly in component's setup function.

I am using Vue 3.2.25 with Vite.js 2.9.9 and the latest version of PrimeVue

[Vue warn]: inject() can only be used inside setup() or functional components.
[Vue warn]: Unhandled error during execution of native event handler 
  at <App>

Uncaught Error: No PrimeVue Toast provided!
    at useToast (usetoast.esm.js:8:15)
    at Proxy.showToast (toastService.js:4:19)
    at _createElementVNode.onClick._cache.<computed>._cache.<computed> (App.vue:4:21)
    at callWithErrorHandling (runtime-core.esm-bundler.js:155:22)
    at callWithAsyncErrorHandling (runtime-core.esm-bundler.js:164:21)
    at HTMLButtonElement.invoker (runtime-dom.esm-bundler.js:369:13)

Here is a CodeSandbox link: https://codesandbox.io/s/prime-vue-toast-issue-owcio8?file=/src/services/toastService.js

Here is my main.js

import App from './App.vue'
import { createApp } from 'vue'
import PrimeVue from 'primevue/config';
import 'primevue/resources/primevue.min.css';
import 'primevue/resources/themes/lara-dark-blue/theme.css';
import ToastService from 'primevue/toastservice';

const app = createApp(App);

app.use(PrimeVue);
app.use(ToastService);

app.mount('#app')

And here is my App.vue

<template>
    <Toast />

    <button @click="showToast">Show toast!</button>
</template>

<script setup>
    import Toast from 'primevue/toast';
    import { showToast } from './services/toastService';
</script>

and here is my toastService.js:

import { useToast } from "primevue/usetoast";

const showToast = () => {
    const toast = useToast();

    toast.add({ severity: 'info', detail:'Hello' });
}

export { showToast }

CodePudding user response:

Vue composables are primarily supposed to be used directly in setup function, when component instance is created. Some of them can be used in other places but this depends on the implementation of a composable and should be additionally confirmed.

The error suggests that useToast uses inject internally, this restricts the usage of this composable.

For a reusable service, it can be:

import { useToast } from "primevue/usetoast";

export const useToastService = () => {
  const toast = useToast();

  const showToast = () => {
    toast.add({ severity: 'info', detail:'Hello' });
  }

  return { showToast };
};

And used like:

const { showToast } = useToastService();

Nothing in PrimeVue Toast implementation actually requires to use useToast composable, it's a convenience helper; see this and this. With some risk of refactoring toast service on the next major library update, it could be simplified to using:

import ToastEventBus from 'primevue/toasteventbus';

export const useToastService = () => {
  const showToast = () => {
    ToastEventBus.emit('add', { severity: 'info', detail:'Hello' });
  }

  return { showToast };
};

This way the service could be used with some discretion in any place of the app, e.g. in a router.

  • Related