1 + 3 Ways to Get Components Talk to Each Other in Vue

Vue provides one excellent way and three other alternative ways of getting your components to work together to form one beautiful, seamless application. Let us look at them one by one :)

1. Vuex

Yesterday we talked about Vuex and how we (try to) manage vuex variables and local variables in a Vue component. What we did not talk about was how Vuex is the most effective solution for all inter-component communications.

Vuex provides a centralized storage that is easy to use, is modular, and can act as a repository for all states, methods and more in your application. You will benefit greatly and keep your application super DRY by evaluating whether your states / functions can be moved to Vuex.

Referring to Vuex state is also a clean affair.

<!-- ComponentA.vue -->
<template>
  {{ userName }}
</template>

import { mapState } from "vuex";
<script>
  export default {
    computed: {
      ...mapState("users", ["userName"])
    }
  };
</script>

Here users is a module in our vuex store and holds all states, methods and beyond for user-related stuff. We refer to one such variable called userName.

Set the Vuex states through mutations within this component or any other component in your app.

Component communication becomes extremely simple when using Vuex -

  1. Component A does stuff, updates a state in Vuex store
  2. Component B, C, or Z have access to the changed state

This communication is multi-directional, can be easily refactored and scaled for current and future components.

2. Props

Props to a component are what arguments are for a function. You can pass props to a component and the target component can make use of it.

The source component will pass variables and target component needs to have definitions of the passed variable.

<!-- Source.vue -->
<template>
  <Target tinyInput="IamTiny" />
</template>

<script>
  export default {
    components: { Target: () => import("./Target") }
  };
</script>

And, the target is -

<!-- Target.vue -->
<template>
  {{ tinyInput }}
</template>

<script>
  export default {
    props: {
      tinyInput: {
        type: String,
        required: false
      }
    }
  };
</script>

As you can make out, this sort of communication is limited in reach but useful for a more localised treatment than Vuex. The direction is always from the caller component (source) to the called component (target). Target has to make use of events to talk back to the source.

3. Events

Events can be used by a component to raise a user or system event to all its friends. The source will raise events, and any target components interested in the event will have to ‘catch’ them.

<!-- Source.vue -->

<template>
  <button @click="raiseClick">Thou Shall Raise</button>
  <button>Cancel</button>
</template>
<script>
  export default {
    data() {
      return {
        planet: "Earth"
      };
    },
    methods: {
      handleClick: function() {
        this.$emit("raiseClickClicked", planet);
      }
    }
  };
</script>

The “catcher” will have following code -

<!-- Source.vue -->
<template>
  <div>
    <Target @raiseClickClicked="greatEventHappened" />
    {{ message }}
  </div>
</template>

<script>
  export default {
    data() {
      return {
        message: ""
      };
    },
    methods: {
      greatEventHappened: function(msg) {
        this.message = "Thou shall raise from this " + msg;
      }
    }
  };
</script>
4. Event Bus

The last event in this post is the event bus. Instead of sending events that a distinct source has to raise and a lone target has to listen to, we make events reach multiple components.

The last example can be changed to -

<!-- Source.vue -->

<template>
  <button @click="raiseClick">Thou Shall Raise</button>
  <button>Cancel</button>
</template>
<script>
  export default {
    //...
    methods: {
      handleClick: function() {
        this.$root.$emit("raiseClickClicked", planet);
      }
    }
  };
</script>

$root represents the root component in the application. We just use it as a channel to reach all interested components.

Any component that wants to listen to this component can have -

<!-- lot of other code -->
<script>
  export default {
    mounted() {
      this.$root.$on("raiseClickClicked", msg => {
        console.log("great event happened", msg);
      });
    }
  };
</script>

The component is registering a call-back telling $root that it is interested to know if there is an event called raiseClickClicked. mounted() just happens to be a convenient place to call such a call back since the component comes into existence with mounted. You could do that anywhere you please.

Final Statements

For inter-component communication -

  • use Vuex.
  • use Prop for extremely local communication, or where using a store may not be possible.
  • use events only for small interactions like opening/closing dialogs.
comments powered by Disqus