Subscribe on changes!

Warning "Extraneous non-emits event listeners" shows up even when emits is set

avatar
Nov 2nd 2020

Version

3.0.2

Reproduction link

https://codesandbox.io/s/vue-event-warning-bug-dqokg

Steps to reproduce

Create a fragment component with setup as render function. Then define a custom event. Listen to the custom event -> Warning should pop up in the console when loading the fragment component.

e.g. an input component as child component.

import { defineComponent, h } from "vue";

export default defineComponent({
  props: {
    msg: String
  },
  emits: ["test-change"],
  setup(props, { emit }) {
    return () => {
      return [
        h("h1", props.msg),
        h("input", {
          onChange: (evt) => emit("test-change", evt)
        })
      ];
    };
  }
});

parent component:

<template>
  <InputComponent
    msg="string input"
    @testChange="(evt) => console.log(evt.target.value)"
  />
</template>

<script>
import InputComponent from "./components/input";
export default {
  name: "App",
  components: {
    InputComponent,
  },
  setup() {
    console.log(InputComponent);
    return {
      console,
    };
  },
};
</script>

What is expected?

The warning should not appear.

What is actually happening?

A warning appears.

Extraneous non-emits event listeners (testChange) 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. 

Events work fine.

avatar
Nov 2nd 2020

Seems to be related to camelCase - kebap-case event name conversions. Both are allowed but this check seems to only verify the on it gets from the parent.

defining it in camelCase works:

emits: ['testChange']

...but both should work ideally.

avatar
Nov 3rd 2020

@test-change="(evt) => console.log(evt.target.value)" is recommended

avatar
Nov 3rd 2020

Hello, I fix the isEmitListener function in PR #2542 and now it works correctly.

: )

avatar
Nov 3rd 2020

Hello, I fix the isEmitListener function in PR #2542 and now it works correctly.

: )

your PR not fix this. warning still shown and works incorrectly. did you test that?

avatar
Nov 3rd 2020

Hello, I fix the isEmitListener function in PR #2542 and now it works correctly. : )

your PR not fix this. warning still shown and works incorrectly. did you test that?

Are you sure? I test the PR and warning not appear. And the unit test I add was passed

截屏2020-11-03 14 08 01
avatar
Nov 3rd 2020

here is my code, is there something wrong ?😭

<script src="../../dist/vue.global.js"></script>

<div id="app">
  <Comp msg="string input" @testChange="(evt) => console.log(evt.target.value)"></Comp>
</div>

<script>
  const { createApp, defineComponent,h } = Vue
  
  const Comp = defineComponent({
    props: {
      msg: String
    },
    emits: ["test-change"],
    setup(props, { emit }) {
      return () => {
        return [
          h("h1", props.msg),
          h("input", {
            onChange: (evt) => emit("test-change", evt)
          })
        ];
      };
    }
  })

  createApp({
    components: {
      Comp,
    },
    setup() {
      console.log(Comp);
      return {
        console,
      };
    },
  }).mount('#app')


</script>

image

avatar
Nov 3rd 2020

here is my code, is there something wrong ?😭

<script src="../../dist/vue.global.js"></script>

<div id="app">
  <Comp msg="string input" @testChange="(evt) => console.log(evt.target.value)"></Comp>
</div>

<script>
  const { createApp, defineComponent,h } = Vue
  
  const Comp = defineComponent({
    props: {
      msg: String
    },
    emits: ["test-change"],
    setup(props, { emit }) {
      return () => {
        return [
          h("h1", props.msg),
          h("input", {
            onChange: (evt) => emit("test-change", evt)
          })
        ];
      };
    }
  })

  createApp({
    components: {
      Comp,
    },
    setup() {
      console.log(Comp);
      return {
        console,
      };
    },
  }).mount('#app')


</script>

image

I think I find the reason. In in-DOM template, the key is onTestchange. And in SFC, the key is onTestChange. I will fix it. thanks!

avatar
Nov 3rd 2020

I found this reason and I don't know how to fix this.😆

avatar
Nov 3rd 2020

I found this reason and I don't know how to fix this.😆

I have two ideas.

The first is to show an additional warning for in-DOM component. As you see, the event you emit in in-DOM template works not fine. That is because in-DOM template is case sensitive.

The second is fix the isEmitListener function, like this:

export function isEmitListener(
  options: ObjectEmitsOptions | null,
  key: string
): boolean {
  if (!options || !isOn(key)) {
    return false
  }
  key = key.replace(/Once$/, '')
  return (
    hasOwn(options, key[2].toLowerCase() + key.slice(3)) ||
    hasOwn(options, key.slice(2)) ||
    hasOwn(options, hyphenate(key.slice(2))) || 
    Object.keys(options).some(item => capitalize(item.replace(/-/g, '')) === key.slice(2))
  )
}

Though the warning will disapper, the event is not worked.....

So I prefer the first one.

avatar
Nov 3rd 2020

@shadowings-zy In DOM, attribute names are always interpreted as lowercase by the browser. So in DOM hyphenized event names have to be used. This has always been a caveat in Vue and there's no good way to "fix" without making event names too imprecise, so you can leave that be.

avatar
Nov 3rd 2020

@test-change="(evt) => console.log(evt.target.value)" is recommended

When I use "@test-change" it does not log the evt. Example

emits: ['testChange']

I use the default eslint preset and this tells me, that custom event names should be kebab-case


When I inspected my error messages all error messages contain the event name in camelcase, even when the event in the code is defined in kebabcase. Maybe the compiler changes the names.

@ll-change="(evt) => handleStringInput(field)(evt)"

Extraneous non-emits event listeners (llChange) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.

avatar
Nov 3rd 2020

@test-change="(evt) => console.log(evt.target.value)" is recommended

When I use "@test-change" it does not log the evt. Example

emits: ['testChange']

I use the default eslint preset and this tells me, that custom event names should be kebab-case

When I inspected my error messages all error messages contain the event name in camelcase, even when the event in the code is defined in kebabcase. Maybe the compiler changes the names.

@ll-change="(evt) => handleStringInput(field)(evt)"

Extraneous non-emits event listeners (llChange) were passed to component but could not be automatically inherited because component renders fragment or text root nodes.

Yes, in Vue3, we will convert the event name in camelcase, see issue #2249

avatar
Nov 22nd 2020

As I am making the UI library with my team, we received multiple bug reports from our users because of the changes against emits, and I am also confused by this. The version of Vue I used is v3.0.2

My code here: picker.vue which receives an event named set-picker-option

here time-picker.vue emitted events to the parent, but this event emitted by picker.vue could not be received by time-picker.vue After I set the event name as @SetPickerOptions it worked just fine. It's kind of weird how the documentation says and the code actually behaves Event name as the documentation says when you emit event name with camelcase it should be received in camelcase, when it's kebab-case then it should be received in kebab-case.

And this worked just fine before v3.0.1 also in this case I don't think breaking change like this should be just a patch instead of a minor version update.

avatar
Nov 23rd 2020

Check this out. Codesandbox, I have attached a link to a reproducible demo indicates the breaking change of event names had impact on my project.

Steps to reproduce. First of all open the console tab on codesandbox.

  1. Adjust the dependency version of Vue to exact 3.0.0
  2. Try to Click on the pager button then observe.
  3. Adjust the dependency version of Vue to 3.0.2
  4. Repeat step 2, then observe.

Some screenshots:

Step 1

Step 1

Step 2

Step 2

Step 3

Step 3

Step 4

Step 4

avatar
Mar 2nd 2021

This may not be the issue that everyone else is having, but I was getting this message because I was using this.$parent.$emit('some-function-name'). Essentially, from the grandchild component, I was telling the parent component to emit to the grandparent component. I was able to get around the warnings by adding the emits option in the parent component.

Here is a snippet from the parent component where I added it in:

export default {
  name: 'PicOfTheDay',
  props: ['error', 'loading', 'pond'],
  emits: ['submitted-date'],
  components: {
    Preview
  },
  methods: {
    checkForPond() {
      const todaysDate = moment().format('YYYY-MM-DD');
      return (todaysDate !== this.potd.date);
    }
  }
}
avatar
Mar 10th 2021

Thanks ! That's the solution ! :)