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
should vuejs2.0 keep the $broadcast, $dispatch event api? No good solution to address point to point parent-child communication in 2.0 #3581
Comments
Because it is general proposal discussion, i have not created fiddle yet. If it is required, I'd like to create to demonstrate. |
This has already been justified on the 2.0 Changes Here's a copy:
The example shown on the upgrading guide is the same as you're talking about About recursive communication: Multiple components can listen to the same event. This event is recognised by the common parent so every child can be aware of it |
@posva , thanks for your information. the bus hub really works well in simple single component point to single component point communication. If there are several components with same component type, there will be problem, unfortunately, this is a normal case. Many cases, what i want to use event is to update a small data belonging to some specific component. Current event bus implementation does not give information on destination or origination node( I have seen _uid of every component, maybe we can use this unique _uid for the event wiring? ), so vuejs2.0 can not support point to point event communication in fact. To be exact, vuejs2.0 event bus only support component type to component type communication? Is there a simple solution to address that requirement: triggered by a event in the tree and update ITS OWN DATA |
@cnweibo I've answered your question with an example on forum. I think this example will fill your needs. |
This is solved by simply vuex, without a complicated events system.
'local' means single component only. Your use case is managing state across This whole 'events vs. shared-states' discussion has been settled months On Thu, Sep 1, 2016, 16:17 cnweibo notifications@github.com wrote:
|
I will now close this issue, because
|
In my opinion $broadcast is not needed, only falling props data-flow. |
|
@cnweibo For the record, the event data can be retrieved. {
template: `<foo @bar="dosomething">`,
methods: {
dosomething(params1, params2, ... and all the event data args) {}
}
} |
Sure it can, what makes you think otherwise? |
@fnlctrl
|
Copy/pasting my own comment from another issue: #2760 (comment) I think removing $dispatch was a terrible idea. This wasn't the first ui framework/library to implement the notion of bubbling actions/events up a visual tree. It's a well established idea. Why take away this functionality on the premise that " being able to dispatch a event that causes side effects in its unknown parent tree sounds like a recipe for trouble to me"? You need to leave this responsibility to the users. I'm sure most have the common sense to use this feature appropriately. It is not a new concept! I really can't see the benefit of this change when this library is built upon long time established web technologies/concepts such as the DOM and DOM events that do in fact bubble up the visual tree and have been doing so for years with no one complaining. Isn't the idea of components something that has been recently embraced thanks to the W3C proposal for web components? In my opinion, It only makes sense that Vue components behave similarly to regular DOM elements in regards to how event handling is done. The proposed alternative to use a global event bus seems illogical to me when something more practical, effective, and easier to understand (due to it being a well established concept for years) already existed. Other proposals in this thread remind me of how EmberJS wants to do it. Passing closure actions as properties to the components down each level of the hierarchy. So tedious and unnecessary! Vuejs was the reason i wrote https://www.npmjs.com/package/ember-component-action-bubbling! Aside from this, I really like your library. But seriously, I think this was a terrible change. |
The event bus paradigm was only proposed as a solution to do the exact same thing, when an event-based architecture is often times inferior to a declarative, state-based architecture. Say, you have an app where the user can log in. With an event-based solution:
This comes with a few problems, though. The biggest one being that components that aren't being rendered in the DOM when the event is fired aren't going to receive the change, along with you not knowing which parts of the application will receive the event at all. In addition, receivers of the event can't possibly know where the event is coming from without additional information given. Excuse my language, but that's a massive clusterfuck I've dealt with and don't care to use again. So let's use a stateful approach:
Everything in the app that relies on this state variable updates accordingly. No events, and it doesn't matter when or where the component is created, because it'll always display the correct information. You could use an event to update the global state, but why do so when you can just... update it? A declarative approach lets you write your components in a way that always appear the same way depending exactly on local/global state regardless of what the user does in your application, without having to listen to things happen. I believe this is what Vue was meant for all along, but akin to most things in software development, it took us a while to figure that out. But I'm damn well glad we did. EDIT: Oh, and don't forget that you can watch for parameters and, say, send an AJAX request, or perform some other action when it changes. e.g., after a user logs in, watch the 'loggedIn' variable, when it's true, load their profile images, or something like that. |
I understand what you're saying, but having some random component modify the global state is, I think, the same thing as having that random component bubble up an event to God knows where. You still run the very same risk of having your application's flow "dirtied" accidentally. There are ways to handle both paradigms in a clean manner and that will usually (and should) end up being the responsibility of the framework's user. There will be certain situations where one mechanism will make more sense than the other. For example, I agree that a logged-in state should be something known to your whole application: global state makes sense. But the button that the application user clicks will usually not handle the logic behind actually logging the user in. That will be something handled higher up the chain. It might be a component, might be a route. The button will likely only need to notify something of the intent to log in. The button, therefore, has no need to modify your global state directly. So now, with the removal of $dispatch, you need the button component to know about some global object that manages the application's user session and notify it directly of the intent. This makes the button tightly coupled to your whole application. Or, you might have the button nested 10 levels deep and you'd have to declare a |
Actually, having to do |
Well obviously just directly changing the state can be problematic, but Vuex solves that issue with mutations and actions. And it's true that some solutions will fit the bill better than others, but I've never encountered a situation where declarative logic wasn't the better option. In your specific case, I'd probably just not make a button specific to logging in, heh. In addendum, if a login-related component is nested that deep for some reason, just have it mutate global state. |
The log in button was just an example. And a vuex store is what I was referring to when I mentioned "some global object". I'd have to look into how vuex works, but I expect you're tightly coupling some random component to the rest of your app's state by simply having to manage a reference to the store. This is not desirable if, for example, the log in button was part of some third party library. I really just wish you didn't take away the *choice * to use either paradigm, especially when event bubbling is widely recognized and therefore easy to comprehend for new contributors on a project. |
It depends on what you mean by "some random component", because a router view component, for example, has full rights to access and commit to the store in my opinion. However, if it's a smaller component for reuse, such as a button, form, or some other UI element, 9 times out of 10, there shouldn't be any logical reason for it to access the store, over using props and events. Since data in a Vue application is top-down, you want to keep as much of your local state as top-level as possible. Deep nesting in itself is a problem to be avoided as much as possible. It's not that much of a bother to propagate events two levels down, but chances are, you might need to rethink your template structure if it goes any deeper than that. That's mostly a tangent, though. Often times, the easier paradigms to understand are the ones that end up getting abused to the end of hell and become unwieldy. A state-based approach is much more straight-forward, as agreed upon by most of us who're currently using 2.0. You're free to continue using 1.0, or to move on to another framework if this approach isn't your preference. |
Exactly my point.
Sometimes, this is not an option.
Not so true when those levels go deeper.
This should be up to your users' discipline.
Mmk. Simplicity and convenience is the reason I was considering switching from Ember to Vue. The team removed a lot of features for the 2.0 release. I honestly agree with all of them. Just not this one. Thank you for your replies. |
@ktsn Alternatives to $broadcast and $dispatch are very simple, this removal has come to improve and got better. |
@rhyek I would like to give my 2ct about a couple of points you raised. Since the discussion has already brushed a number of topics, I would like to get back to the basics about why we deprecated $diospatch and $broacast: 1. implicit coupling.If you have a parent and a deeply nested child that dispatches an event, there's no way to infer this relationship from the code (and the same is true for If you look at the other changes and deprecations we introduced with Vue 2.0, you might realize that removing implicit behaviour in favour of explicit alternatives is a common theme, and deprecating Example: // parent
events: {
'some-event': function () { ... }
}
// deeply nested child:
$dispatch('some-event') This is fine when the parent only has one direct child - but in that case, It becomes hard to follow (especially in teams) as soon as you have nested children (especially deeply nested), or even more than one direct child - you either have to look through all children, or rely on code comments to document which event is tiggered from which child component - which is additional boilerplate, as well. You say that you like $dispatch and $broadcast because you don't have to pass them through other components. And I can agree that it's easier - but we have not come across many situations where this was actually nessessary, or rather: if there was such a chain of passing up an event, it would rather be the case that the data would be changed/appended/ during this trip by component in between. 2. Event nameswhen you use // parent
events: {
'close': function () { ... }
}
// deeply nested child 1:
$dispatch('close')
// deeply nested child 2:
$dispatch('close') ..and if those children are third-party-libs, your screwed now. or have to catch the event in some component in the middle just to rename it before you Using $emit and template listeners does not have this problem. you can use simple, short event names everywhere, they won't clash as each event has it's own callback attached in the template
If we thought both paradigms can work equally well, we would treat them equally. But we don't think that, for the above reasons and more. Therefore, we try to steer users to the pactices we found to work best, while leaving a way to get around it with the "global Bus" method. I would also like to talk about your worries about global state, but am not sure I understand your position fully yet. Maybe you can provide an example where you think $dispatch and $broadcast work best for you, and I try to show you how "our" approach could improve the situation? |
I think $dispatch/$broadcast and event bus address different things. They can make code easy to maintain and decoupled on different scenarios. If we can keep them both in, it will be great. |
@cnweibo I think there has been pretty exhaustive arguments on both sides, and honestly I don't see your point about "addressing different things". Feel free to make further arguments, but I can tell you with certainly this will not happen. If you really really want it, it's not that hard to implement it yourself as a plugin. |
@LinusBorg I truly appreciate the time you took to write your reply and I understand your arguments.
I just like
I agree with that and it's usually what I do, anyways. It's also what people are used to doing on jQuery. Furthermore, some platforms just send a "source" object as an argument to the handler and you could maybe filter contexts based on that (hint: In any case, I just honestly don't understand why this is such a concern to the VueJS team. If people can't be careful enough about using
Honest question (as I'm decidedly new to Vue), aside from declaring listeners at every level, don't you also have to In conclusion, let me quote something someone said on an issue on vue-cli about introducing "official templates" for new projects (vuejs/vue-cli#123 (comment)):
I agree with that philosophy, but it's not the same attitude I've encountered about this issue, strangely. The contrast I see between that comment and the comments here boils down to freedom. You're taking away choices based on, although good, ultimately flawed intentions.
@yyx990803 That's probably what I'll end up doing... probably. |
@LinusBorg also:
You're not "trying to steer", you're forcing. :) |
Have you tried implementing the same feature with both |
@posva I plan to, but I mean, you basically declare the event bus object on some module, and you import it wherever else you want to |
Well, the point is that no one in the team could point out a real-life usecase for This might be a subjective view, but maybe you can understand that if the whole team thinks "this is, in in our experience, always a inferior solution", we drop it from the core. At this point, I want to renew my offer to discuss a real example.
This is of course a delicate question and a hard balance to find. We will have to judge case ´by case.
Since I talk about direct parent-child-releationships, you would only have to $emit() once. If you have deeply nested children, of course you have to re-emit at every level, but to repeat myself: We haven't found nor been presented situations were dispatching across many nested children is really nessessary or preferable to other solutions.
I'd say we make your life a little harder - you can
We are not forcing you to use |
I think you can also add it to every Vue instance: |
I LIKE that. Pretty clever.
I think that's a pretty valid criteria for choosing to use something or not, at least for me. In any case, I've stated many times why I think event bubbling has its place.
I mean, do I really need to? I've given examples about situations where I don't like the idea of using an event bus or declaring listeners at every level. You want to see code? I could maybe come up with something to make my point a bit clearer, but I feel event bubbling is a pretty standard thing that most people can appreciate as something useful, and event buses or state managers are, at least to me, a kind of paradigm shift that, although not hard to comprehend by any stretch, seem like hipster territory. 😄 I'm of course kidding about that last comment. Like I stated before, I do see their uses and will definitely find a problem to solve with them. Actually, on some projects I've worked on using Ember, I tend to write a "service" that acts exactly like a global state manager. I assure you, I'm not trying to be stubborn about this on purpose. I really like Vue. I just want to love it, you know? |
You do if you think in terms of event bubbling. And you don't if you use component composition. E.g.: <div>
<a-button @click="modalShown = true">Open modal</a-button>
<a-modal v-if="modalShown">
<a-button @click="modalShown = false">Close modal</a-button>
</a-modal>
</div>
|
I think I'm just repeating myself at this point. I'll end up working around this issue, somehow. It just seems strange to me that this discussion has revolved around stating several workarounds to something pretty standard and practical that just doesn't exist anymore for whatever reason.
This sounds to me like a civil engineer asking me for a reason to not close off a highway exit: He says something like, "This off-ramp leads to an intersection where people get confused about whether to turn left or right. Most people know the way because they've lived here for years, but new citizens often get lost for a couple of minutes and we want to avoid that." I say, "Alright, sure, you can close it, that's cool. I'll just have to travel 10 km further to the next off-ramp. I'll manage." :) Thank you all for your replies. I'm glad you're all open to discussion, at least. Seems like a good team. |
I'll add my bad methaphor aswell. :) In my view it's more like:
I think @posva meant: "I like this" is not a constructive argument when discussing weither to keep something in, or add something to the library with a wide range of users. That's why I keep asking for a valid, real-life usecase to discuss instead of personal preferences. |
Yeah, except you blew up the perfectly good highway and built another with rubberized asphalt because you read somewhere it's pretty neat. Also, it's an extra 10 km. :) About the example: I think the one posted on the original question about recursive components is an ok one. Just imagine you want to do something at every level in sequence when an event is caught and you have a perfectly good example. Doing that with $dispatch is pretty straightforward. |
Yes, please |
@posva I gave an example on the previous comment. Note: if you have to think more than a few seconds how to do that without $dispatch, it proves my point. |
with // recursive-child
<template>
<recursive-child></recursive-child>
<button @click="dispatch">Do something</button>
<template>
<script>
export default{
methods: {
dispatch() { this.$dispatch('do-something') }
},
events: {
'do-something': function () {
// do something, or don't
return true // nessessary to make the event bubble up further. Don't like the un-expressivness of this
}
}
}
</script> with // recursive-child
<template>
<recursive-child @do-something="doSomething"></recursive-child>
<button @click="doSometing">Do something</button>
<template>
<script>
export default{
methods: {
doSomething() {
// do someting, or don't
this.$emit('do-something')
}
}
}
</script> Is that really worse? Got another one? :) |
Ok, sure. What if they're not recursive yet still nested like that? Alright. It's 5am here and I'm going to have a rough day thanks to you. If I come up with something better later I'll post it if not, then, either you've won or I lost interest :) |
You would have to add a This can be seen good or bad, depending weither you stress the verboseness vs. the expressiveness. and btw. if in a situation you have to chain an event up till the parent this can be done something like: <child-comp @event="$emit('event', $arguments)>
Anyways, nice discussion, enjoy your well-deserved sleep. |
@rhyek it looks like workarounds only because you have determined to use Also note DOM event bubbling is fundamentally different from cross-component communication. The argument that "event bubbling is widely recognized" doesn't mean it must be a good solution for the problem we are trying to solve. I'm done commenting in this thread because I find it hard to argue with "I just don't like it". |
@yyx990803 ,
Hi, vuejs core team,
I am struggling on find a good solution for point to point communication between parent child components with vuejs2.0.
Because vuejs2.0 has deprecated the $broadcast,$dispatch api, it is very hard for me to find an alternative solution $broadcast,$dispatch provides with vuejs2.0 event bus feature.
I have written a thread in the forum about this topic here, and I'd like to paste it here for more discussion.
PLEASE DO NOT CLOSE IT UNLESS have a good idea or solution on vuejs2.0. Thanks~!
In above code, when child of pcom1, for example, soncoma $emit a event, say,
on the pcom, we listen that event with
I only want pcom1 will handle that event, but unfortunately pcom2 will also handle that.
How to tackle this condition?
One of the workaround in my application is to use the this.$parent as event bus
In child:
In parent:
The negative point for above workaround is:
In some more complex conditions, it will be more difficult to find a good solution for the custom event system, for example, a recursive component
how recursivechild communicate to its direct pcom component?
Please give your idea or point on these topics.
Thanks~!
The text was updated successfully, but these errors were encountered: