Vue.js Components Communication Patterns (without Vuex) - Part 3
Tags: vue, javascript, webdev
Published: 2021-11-23 12:29:41

***Upward-broadcast and Downward-broadcast patterns***

In [Part 2](https://csdigitalnomad.medium.com/vue-js-components-communication-patterns-without-vuex-part-2-7f9c0bc132a2) of this series, we used the "$parent" and the "$children" patterns to enable components to communicate in a three-level hierarchy. Towards the end, we had a question in mind for an even more complex hierarchy of components, e.g. what if there are ten levels of components? Do we need to do something like:
```javascript
this.$parent.$parent.$parent.$parent.$emit('anyEvent', args);
```
Let alone the children components can be hard to track.

This leads to the pattern to be introduced here in Part 3.

First, all the example components we have worked through form a data structure called Binary Tree. Below is the graphic from the Vue documentation for nested components in the computer memory when a Vue app is running: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oozxxgcurdbsi0aeua2n.png)

Here is another binary tree with more levels:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p20xytuqw975v4mk5fhu.jpg)

Now suppose we have these components below nested together:

> `App`
>> `Parent`
>>> `ChildA`
>>>> `GrandchildA`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7xeu4mc10cfy0hd655tx.png)

In `App`, we bind an event named `change:font` onto `Parent`, the callback function is `handleChangeFont` which will update the font size for `Parent` and its descendants, according to the argument size passed in.

`Parent` is the same as previous examples, it maintains a list of `desserts` and passes it to `ChildA`:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdd2zsxog0vfm8vkyq29.png)

`ChildA` and `GrandchildA` are simple, `ChildA` receives the prop and passes it down to `GrandchildA`:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/el88mbjc4vjve0hswjmn.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/626uksm0o29l5z966fej.png)

And this is what the view looks like at the moment:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fbeqtn8h4rw9ufjm016h.png)

Now for some business requirements, users want to change the font size by clicking a button named `Change Font Size` in `GrandchildA`:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4hggpfbvlnyrt8qmtvgf.png)

We can do so by utilizing the "$parent" pattern from [Part 2](https://csdigitalnomad.medium.com/vue-js-components-communication-patterns-without-vuex-part-2-7f9c0bc132a2):

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8snv7tlkxzlv684ngym.png)

Line 30 is the "$parent" pattern in action. Click on the button, the font size on the view becomes 20px:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9n49ccqqyvkq4y14vm1j.png)

Awesome!

But as you can see, with the complexity of the nesting, we would have to code something like:
```javascript
      this.$parent.$parent.$parent.$parent.$parent.$emit('change:font', '20px');
```
if users want to add that button in a grand-grand-grand-grandchild component.

A better solution in a large tree structure would come in handy.


## Upward broadcast pattern
If a component wants to inform an ancestor component via triggering one of its events, this component can literally "broadcast" upward to all its ancestors about an event it wants to trigger, and if one ancestor component has that specific event registered, it will perform the callback function automatically. We can implement this event mechanism on the `Vue` prototype object, so all the `VueComponent` instances can have access to it via `this`.

In `main.js`, let's create a function named `$upwardBroadcast` on `Vue.prototype`:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/889gzb7xs91umb42cz42.png)

The `$upwardBroadcast` function has two parameters:
* `event`: the event being broadcast upward from the current component
* `args`: the data being passed when emitting the event

It will broadcast an event from the current component upward to all the ancestors, if one ancestor in the upward tree hierarchy has that event registered, it will respond and execute the callback function registered alone with the event. Let's implement it:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/go1ra40u18ehxhfyal5p.png)

First, at line 12, we save the parent of the current component. In lines 12 - 16, if the parent exists, it will use the parent instance to emit the event, then proceed to the parent's parent, and the parent's parent's parent, etc.
The `while` loop stops when there is no parent anymore, meaning it has reached the top (root) node on the tree.

Now let's see how to use it to improve the previous "$parent" pattern in `GrandchildA`. Very simple, just one line of change:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/witatov5ey3f3ennyfum.png)


Line 31 replaces line 30 and uses the `$upwardBroadcast` function via `this`, and it broadcasts the event `change:font` and passes the argument `'20px'`. If we click the button, the font size changes as before:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yhv2i2tpcv359d250j25.png)


## Special Note
Here I say "uses the `$upwardBroadcast` function **via** `this`", not "**on**" `this`, because `$upwardBroadcast` is not defined on the `VueComponent` instance created from the `VueComponent` constructor function, but on the `Vue` constructor's prototype - as what we did in `main.js`. Yes, a better understanding of Vue.js requires a solid foundation of JavaScript basics, that's why I like Vue so much - you are not just using a framework to do the work, but you get to consolidate and deepen basic knowledge of JavaScript.

But if you think about it a bit - how come a `VueComponent` instance can access the `Vue` constructor's prototype?Actually, Vue did one thing on top of the JavaScript prototype chain - it modified where `VueComponent.prototype` points.

Also, the function name starts with a `$` sign, and this is only because this is the convention of all the built-in properties and methods in Vue.js.


## Downward broadcast pattern
Now let's implement a downward broadcast mechanism. In `main.js`, let's create another function named `$downwardBroadcast` on `Vue.prototype`:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ci2imotaxxok8jv6woyb.png)

It has the same two parameters as `$upwardBroadcast`, and it will broadcast an event from the current component downward to all the descendants, if one descendant in the downward tree hierarchy has that event registered, it will respond and execute the callback function. We can do so:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/afe40ir7h049knmsn9ky.png)

First, we get all the descendants of the current component, and for each child, it will emit the event. Here what is different from one child only having one parent in `$upwardBroadcast`, is that now each child can have many children, so if there are any children components of a current child, we need to repeat the same logic, as seen in line 28.

This is the perfect case for recursion, and let's implement it:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zu1nag2vx1fed7562vey.png)

In the function body, we create another function called `downwardBroadcast`. First, we execute this function by passing in the current component's `this.$children` array, as seen in line 33. Then within `downwardBroadcast`, we loop through the children array, and if there are children under the current child, we will execute `downwardBroadcast` again, passing in the current child's `$children`.

Now our `main.js` looks like this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cuya8in2bp6pvir7vqbk.png)

Time to see it in action. We will downward broadcast an event named `show:year` in `App` to all its descendants after clicking a new button named `Display current year`, and the argument passed in is the current year:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pilgmyaudc2m546rcoka.png)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2kgsvmbf5e8lobk2wfi.png)

In `ChildA`, we bind this event onto `GrandchildA`, the callback function is `ChildA.showYear()`:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sk6p1rdl4ehnnh5goyad.png)

Click on the button, the alert window is shown:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hw3xlo48vd2mvl01kkwc.png)

The broadcasting is powerful, isn't it?

## Encapsulate the functions ( Hooks / Composition style)
One thing we can improve is to move the functions in `main.js` into a separate file - `src/hooks/events.js`, 
so this file contains functions that enhance the event system on `Vue.prototype`:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qlwav1hbelxbyhgzcopz.png)

Following the naming convention of Hooks or Composition API, we create two new functions named `useUpwardBroadcast` and `useDownwardBroadcast`, the parameter is the `Vue` constructor function. Within each function body, it is the previous function defined.

Now in `main.js`:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bqqdxp0g9mc4cbpgbcem.png)

we can import these two functions and run them to enhance `Vue.prototype`, if we need.

In the next part of this series, we will explore another mighty Vue.js components pattern.

Here are all the articles in this series:

***[Vue.js Components Communication Patterns (without Vuex) - Part 1](https://csdigitalnomad.medium.com/vue-js-components-communication-patterns-without-vuex-part-1-e9da6a0285ac)***

***[Vue.js Components Communication Patterns (without Vuex) - Part 2](https://csdigitalnomad.medium.com/vue-js-components-communication-patterns-without-vuex-part-2-7f9c0bc132a2)***

***[Vue.js Components Communication Patterns (without Vuex) - Part 3](https://csdigitalnomad.medium.com/vue-js-components-communication-patterns-without-vuex-part-3-5ccfdbe3ecfa)***

***[Vue.js Components Communication Patterns (without Vuex) - Part 4](https://csdigitalnomad.medium.com/vue-js-components-communication-patterns-without-vuex-part-4-f99a17bd98f0)***

***[Vue.js Components Communication Patterns (without Vuex) - Part 5](https://csdigitalnomad.medium.com/vue-js-components-communication-patterns-without-vuex-part-5-2cf6fb1c5e26)***

***[Vue.js Components Communication Patterns (without Vuex) - Part 6](https://csdigitalnomad.medium.com/vue-js-components-communication-patterns-without-vuex-part-6-10e4e43f0ac7)***

***[Vue.js Components Communication Patterns (without Vuex) - Part 7](https://csdigitalnomad.medium.com/vue-js-components-communication-patterns-without-vuex-part-7-15c5b786539e)***