Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
milleniumfrog opened this issue Nov 2, 2020 · 17 comments · Fixed by #2542
Closed
Labels
has PR A pull request has already been submitted to solve the issue 🐞 bug Something isn't working

Comments

@milleniumfrog
Copy link

milleniumfrog commented Nov 2, 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.

@LinusBorg
Copy link
Member

LinusBorg commented Nov 2, 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.

@LinusBorg LinusBorg added the 🐞 bug Something isn't working label Nov 2, 2020
@LinusBorg
Copy link
Member

LinusBorg commented Nov 2, 2020

@edison1105
Copy link
Member

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

@shadowings-zy
Copy link
Contributor

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

: )

@edison1105
Copy link
Member

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?

@shadowings-zy
Copy link
Contributor

shadowings-zy commented Nov 3, 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

@edison1105
Copy link
Member

edison1105 commented Nov 3, 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

@shadowings-zy
Copy link
Contributor

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!

@edison1105
Copy link
Member

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

@shadowings-zy
Copy link
Contributor

shadowings-zy commented Nov 3, 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.

@LinusBorg
Copy link
Member

LinusBorg commented Nov 3, 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.

@LinusBorg LinusBorg added the has PR A pull request has already been submitted to solve the issue label Nov 3, 2020
@milleniumfrog
Copy link
Author

@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.

@shadowings-zy
Copy link
Contributor

@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

@jw-foss
Copy link

jw-foss commented Nov 22, 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.

@jw-foss
Copy link

jw-foss commented Nov 23, 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

@richardltyler
Copy link

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);
    }
  }
}

@effset
Copy link

effset commented Mar 10, 2021

Thanks ! That's the solution ! :)

@github-actions github-actions bot locked and limited conversation to collaborators Oct 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
has PR A pull request has already been submitted to solve the issue 🐞 bug Something isn't working
Projects
None yet
7 participants