91

I am trying to emit data from child to parent using the composition API

I get the following warning.

[Vue warn]: Extraneous non-emits event listeners (updatedcount) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.at <HelloWorld onUpdatedcount=fn > at

childcomponent.vue


<template>
  <h1>{{ store.count }}</h1>
  <button @click="fired">click me</button>
</template>

<script>
import useStore from "../store/store.js";
export default {
  name: "HelloWorld",
  setup(_,{ emit }) {
    const store = useStore();

    const fired = () => {
      store.count++;
      emit("updatedcount", store.count);
    };

    return {
      store,
      fired
    };
  },
};
</script>


parentcomponent.vue


<template>
  <div>
    {{ hello }}
    <br />
    <br />
    <input type="text" v-model="hello.searchQuery" />
    <br><br>
    <button @click="hello.count--">click me too!</button>
    <hello-world @updatedcount="mydata" />
  </div>
</template>

<script>
import HelloWorld from "./components/HelloWorld.vue";
import useStore from "./store/store.js";

export default {
  components: {
    HelloWorld,
  },
  setup() {
    const hello = useStore();

    function mydata(event) {
      console.log(event);
    }

    return {
      hello,
      mydata
    };
  },
};
</script>

Fanna1119
  • 1,878
  • 4
  • 24
  • 30
  • The accepted answer works great, but also you can just wrap your child component in a single div too. – Coz Oct 16 '22 at 15:45

6 Answers6

131

I think you need to define the emits in your component: https://v3.vuejs.org/guide/component-custom-events.html#defining-custom-events

export default {
  name: "HelloWorld",
  emits: ["updatedcount"], // <--- add this line
  setup(_,{ emit }) {
    ...
  },
};
Kos
  • 4,890
  • 9
  • 38
  • 42
Thomas
  • 6,325
  • 4
  • 30
  • 65
  • @Fanna1119 Also took me a while to find it again, so don't blame yourself ;) – Thomas Oct 06 '20 at 08:56
  • 5
    Note that it's probably best for now to name the event the way you did - no hyphens, just all lowercase letters. If you name it kebab-case, the runtime complains that you didn't declare it as an emit in camelCase; on the other hand, if you name it camelCase, EsLint complains because events are supposed to be kebab-case. – Tobias Feil Nov 20 '20 at 11:47
  • 2
    Thank you, Creator of the Universe, for creating SoF, so that good people can post such good answers. – Michael Zelensky Aug 03 '21 at 14:13
  • it works for me. thanks – SefaUn Jan 13 '22 at 21:31
  • 1
    @MichaelZelensky I think you mean thank you [Jeff Atwood and Joel Spolsky](https://en.wikipedia.org/wiki/Stack_Overflow) – hitautodestruct Jan 20 '22 at 08:17
  • @hitautodestruct: in the grand scheme of things, the inception of SO is dependent on the rise of technology and programmers -> first programmers and electrical engineers -> invention of electricity -> advancement in science -> evolution of human civilization -> ... -> first cause. In that sense, Tobias is right, but he probably meant to thank the Universe/Luck/Creator/First Cause/whatever that SO exists right now and is so accessible and useful – Prid Jun 02 '22 at 19:08
  • I'm not sure about this answer. You *can* define emits, but the documentation says `A component can emit custom events directly in template expressions (e.g. in a v-on handler) using the built-in $emit method`. It doesn't say *anything* about having to define emits to avoid some warning in the console. – Jez Sep 16 '22 at 10:27
  • @Jez But OP is not using it in the template directly, but in the setup method. Yes he could do the increment and the emit directly in the template, but I also prefer to have as little code in the template as possible and always use functions or computed values. – Thomas Dec 01 '22 at 12:19
17

update:

in vue 3 script setup you would do

const emits = defineEmits(["updatedcount"])

emits("updatedcount", store.count);
Fanna1119
  • 1,878
  • 4
  • 24
  • 30
17

When seeing this error in my own vue 3 apps I've found that wrapping a component's template content in an empty div fixes the issue for me which I believe relates to the "could not be automatically inherited" portion of the error message.

It seems the way vue works is that vue will attempt to use attribute inheritance for common events like @click and @input to pass to underlying elements but when there are multiple sibling elements at the root of a component it doesn't know which one to choose.

Do be warned that these events could change some behavior since the new wrapping parent div will now have events tied directly to it.

<template>
  <div>
    <h1>{{ store.count }}</h1>
    <button @click="fired">click me</button>
  </div>
</template>
Matt
  • 542
  • 5
  • 6
1

In vue3 you have to define emits, your child component would look like this :

childcomponent.vue:

    <template>
      <h1>{{ store.count }}</h1>
      <button @click="fired">click me</button>
    </template>
    
    <script>
    import useStore from "../store/store.js";
    export default {
      name: "HelloWorld",
      emits :{
          updatedcount: null, <--- add this lines
        },
      data: () => ({
            store: useStore(),
      }),
      methods:
        fire () {
          store.count++;
          this.$emit("updatedcount", store.count);
        },
      
    };
    </script>
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 20 '22 at 10:26
0

Additionally, the "Extraneous..." warning often indicates that you attempted to assign a prop, attribute, or event listener (@click, @focus) to a custom component or a library component that does not support it.

If you set a breakpoint on the warning, you can easily identify where the problem is located by tracing back the stack trace.

snoob dogg
  • 2,491
  • 3
  • 31
  • 54
0

This is probably old. I had the same problem. It went away by wrapping everything in the template in a tag (single root). I'm using Vue3

Make the the root tag.

<template>
  <div>
    <button @click="$emit('toparent')">Click Me</button>
    <p>Some text</p>
  </div>
</template>