Vue-class-component: How to create functional component in @Component?

Created on 5 Jul 2017  路  9Comments  路  Source: vuejs/vue-class-component

Hi guys,

How to create functional component in @Component?

import * as Vue from 'vue'
import Component  from 'vue-class-component'

@Component({
    props: ["a"]
    functional: true
})
export default class XXX extends Vue {

}

It prompts error in Visual Studio Code:

[ts] Argument of type '{ functional: boolean; }' is not assignable to parameter of type 'VueClass'.
  Object literal may only specify known properties, and 'functional' does not exist in type 'VueClass'.
enhancement

Most helpful comment

@marcosmoura I think adding the additional decorator @FunctionalComponent is safer. Union type cannot deeply predict which type to use when assigned, for example in using this.$refs

// will error because typescript will assume the type of $refs is Element[]
this.$refs.upload.active = true

// this will work
const upload = this.$refs.upload as MyComponent
upload.active = true

So if it use ComponentOptions<Vue> | FunctionalComponentOptions<Vue> then the options object parameter must be typecasted

@Component({
  functional: true
} as FunctionalComponentOptions)
export default class Stylesheets extends Vue {}

@ktsn In my opinion, adding support for functional based class can enhance the usage of template tag for functional component (especially in validating props value)

<template functional>
  <rect :width="props.size" :height="props.size">
    <slot></slot>
  </rect>
</template>
import { Prop, FunctionalComponent, Vue } from 'vue-property-decorator'

ensureSize(val: number) {
   return val > 10 && Math.sqrt(val) < 300
}

@FunctionalComponent()
export default class Square extends Vue {
  @Prop({ default: 50, validator: ensureSize })
  size: number
}

All 9 comments

We need to declare an additional type for @Component decorator. Thanks for reporting this.

I just wondered the benefit of using @Component decorator for functional components because this is undefined in a functional component.

Oh, indeed.

@lijialing888 Why do you want to use @Component for function components?

Closing since there would not be benefit to use class style syntax for a function component as @kaorun343 described.

@ktsn
Let's suppose that I don't want to render any template or I need an empty render function, but still have the ability to use the decorator while creating functional components. Look at this example:

import { Component, Vue } from 'vue-property-decorator'

@Component({
  functional: true,
  props: {
    theme: {
      type: String,
      default: 'default'
    }
  }
})
export default class Stylesheets extends Vue {
  // A bunch of Vue lifecycle methods here and there...
  render () {
    return null
  }
}

The functional: true will raise an error saying that the functional property is not assignable of type VueClass<Vue>.

In the utils.ts file, there is the following line:

export function createDecorator (factory: (options: ComponentOptions<Vue>, key: string, index: number) => void): VueDecorator {

The type is ComponentOptions (that do not have functional). By changing the line including the functional component type, the decorator works just fine and will inject the functional: true.

export function createDecorator (factory: (options: ComponentOptions<Vue> | FunctionalComponentOptions<Vue>, key: string, index: number) => void): VueDecorator {

Does it make sense to change it to something like that? There is some side effects that this could raise?

Or maybe a safer @FunctionalComponent with the same goods as @Component...

If so, a PR on this matter is welcome?

@marcosmoura I think adding the additional decorator @FunctionalComponent is safer. Union type cannot deeply predict which type to use when assigned, for example in using this.$refs

// will error because typescript will assume the type of $refs is Element[]
this.$refs.upload.active = true

// this will work
const upload = this.$refs.upload as MyComponent
upload.active = true

So if it use ComponentOptions<Vue> | FunctionalComponentOptions<Vue> then the options object parameter must be typecasted

@Component({
  functional: true
} as FunctionalComponentOptions)
export default class Stylesheets extends Vue {}

@ktsn In my opinion, adding support for functional based class can enhance the usage of template tag for functional component (especially in validating props value)

<template functional>
  <rect :width="props.size" :height="props.size">
    <slot></slot>
  </rect>
</template>
import { Prop, FunctionalComponent, Vue } from 'vue-property-decorator'

ensureSize(val: number) {
   return val > 10 && Math.sqrt(val) < 300
}

@FunctionalComponent()
export default class Square extends Vue {
  @Prop({ default: 50, validator: ensureSize })
  size: number
}

Does anybody solve this problem? I trying to make recursive scoped slots for tree-component and I think that I have to access native render method for injecting $scopedSlots property next to children components. What do you think about it, guys?

Any solution for this?

Just use Vue.extend...

import Vue from 'vue'

export default Vue.extend({
  functional: true,
  render(h, ctx) {
    // ...
  }
})

As it's already pointed out the former of this issue, using class style syntax for functional component does not have benefit since functional component does not have its instance. It won't included in this lib since class style functional component does not follows standard class semantics (not have instance, this is undefined ...).

Was this page helpful?
0 / 5 - 0 ratings