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

Recursive compontent that emits cannot be created #3361

Open
kevinvalk opened this issue Mar 4, 2021 · 4 comments
Open

Recursive compontent that emits cannot be created #3361

kevinvalk opened this issue Mar 4, 2021 · 4 comments
Labels
has workaround A workaround has been found to avoid the problem need guidance The approach/solution in the PR is unclear and requires guidance from maintainer to proceed further.

Comments

@kevinvalk
Copy link

Version

3.0.7

Reproduction link

https://codesandbox.io/s/recursive-emit-bug-10073

Steps to reproduce

See codesandbox for illustration of the problem

  • Create a recursive component that has an 'emits: ["event"]'
  • Emit a custom event $emit("event", depth) from recursive component
  • Add v-bind="$attrs" to every recursive inclusion from recursive component to enable emitting from any level
  • Finally use the recursive component with an @event="..." handler

What is expected?

The @event="..." is called for any "depth"

What is actually happening?

Only the first (top level) of the recursive component is handled


I spent some hours debugging this issue. According to documents we should add emits: ["<name>"] to our components. However, by doing so, we capture the event listener and effectively remove it from $attrs at the first "level".

The system behaves according to specifications, but it feels wrong to have to leave out emits: ["<name>"] when you build an emitting recursive component. At least for me it was very counter-intuitive.

Sadly I do not have a good solution as I understand why you capture any defined props and emits from the $attrs. However, maybe I am just not doing it right, would love to hear how we should create an emitting recursive component.

P.S. I found this problem when I was creating a file explorer component using a single recursive component.

@HcySunYang
Copy link
Member

See https://codesandbox.io/s/recursive-emit-bug-forked-weqfy?file=/src/components/RecursiveEvent.vue

@kevinvalk
Copy link
Author

kevinvalk commented Mar 5, 2021

Thanks for the information! I thought that solution (re-emitting event @event="(depth) => $emit('event', depth)") was suboptimal because of call stack depth and performance reasons. For posterity: I was partially wrong on that front https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/

TL;DR; If you use ES6 and have strict mode on, Tail Call Optimization is used. Essentially it changes a returning function call at the end of a function into a jump. However, the system still emits (potentially many times) events decreasing performance.

@HcySunYang
Copy link
Member

Yeah, I did also aware of the problem. I think I should reopen this issue for further discussion.

@HcySunYang HcySunYang reopened this Mar 5, 2021
@HcySunYang HcySunYang added the has workaround A workaround has been found to avoid the problem label Mar 5, 2021
@HcySunYang HcySunYang added the need guidance The approach/solution in the PR is unclear and requires guidance from maintainer to proceed further. label Apr 6, 2021
@loriswave
Copy link

loriswave commented Oct 6, 2022

Hi,
so as summary there is two way for recursive nesting component:

  1. Emits from Template and not have defineEmits on the script
  • Template of recursive nested component
    • Where the event is generated:
      • @click.stop="$emit('start', element)"
    • Where the children that is nested:
      • v-bind="$attrs"
  • Template of grand parent
  1. Emits normally from script but have a recieve/sender of the event in each node
  • Template of recursive nested component
    • Where the event is generated:
      • @click.stop="handleClick($event, element.node)"
    • Where the children that is nested
      • @start="(node) => $emit('start', node)"
  • Script
    • const emit = defineEmits(['start'])
    • function handleClick(event:any,node:Node) { emit('start', {event, node}); }
  • Template of grand parent

is this correct?
can you give me a suggest of which is the best way?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has workaround A workaround has been found to avoid the problem need guidance The approach/solution in the PR is unclear and requires guidance from maintainer to proceed further.
Projects
None yet
Development

No branches or pull requests

3 participants