Bryan Lee
Bryan Lee

Musings about growth, software engineering, and tech.

Javascript

Passing functions as props an anti-pattern in Vue.js

In React you can pass down functions as props from the parent to the child component. The function call then flows back up from the child to the parent, faciliating parent-child component communication. This is also doable in Vue.js using code like this:

<template>
  <Child :callback="doSomething" />
</template>

<script>
  export default {
    name: 'Parent',

    methods: {
      doSomething() {
        //
      }
    }
  }
</script>

And in the child component, you receive the function as a prop:

<template>
  <a @click="execute">Execute action</a>
</template>

<script>
  export default {
    props: {
      callback: {
        type: Function
      }
    },

    methods: {
      execute() {
        // ... do something here

        if (this.callback) {
          this.callback()
        }
      }
    }
  }
</script>

Vue.js custom events as an alternative

While this would work perfectly, this is mostly considered an anti-pattern in Vue. By passing functions down as props, you are linking both parent child components together with two-way data binding. In the example above, the child now needs to know about the context of the function prop from its parent. Every time execute runs, it needs to check for and execute the callback function passed down.

As you reuse this child component, you start having different callback functions passed down from different parents. As the application grows larger in size and other developers come on board, they will look at the child component code and have to figure out which callback function prop it is and where it is from.

Instead, Vue.js has a custom events system that accomplishes the same thing:

# Parent
<template>
  <Cat @onHappy="doSomething" />
</template>

<script>
  export default {
    name: 'Parent',

    methods: {
      doSomething() {
        //
      }
    }
  }
</script>

# Child Cat component
<script>
  export default {
    methods: {
      eats() {
        this.$emit('onHappy')
      }
    }
  }
</script>

Whenever the eats method in the Cat component is called, it emits an onHappy event. The parent can then listen for the onHappy event and call a corresponding function. As the data flows in only one way, it makes for much easier debugging too.

The case for passing functions as props

One advantage of passing functions as props is that it makes the code much cleaner and DRYer. Instead of emiting and listening for events, you pass down a function and have it called from the child. While in most cases this is not a good idea, if you have two components that have to be coupled together in any case and the child component does not get reused in other scenarios, this may be a good approach to consider. With that said, events should be the main approach most of the time and this should be used with utmost caution.

    comments powered by Disqus