3

I wrote a "loading state" mixin for Vue 2:

export default {
  props: {
    loading: {
      type: Boolean,
      default: false
    },
  },
  data () {
    return {
      innerLoading: false,
    }
  },
  mounted () {
    this.innerLoading = !!this.loading
  },
  methods: {
    startLoading () {
      this.$emit('update:loading', this.innerLoading = true)
    },
    stopLoading () {
      this.$emit('update:loading', this.innerLoading = false)
    },
  },
  computed: {
    isLoading () {
      return !!this.innerLoading
    },
    isNotLoading () {
      return !this.innerLoading
    },
  },
  watch: {
    loading (loading) {
      this.innerLoading = !!loading
    },
  }
}

I use this mixin for other components to hold the loading state. For example for forms, buttons, tables etc.

Now, Im trying to rewrite this mixin to composition API style for Vue 3. Ideally, I would like to use my loading composable like this:

// components/Table.vue

import 'useLoading' from 'src/composables/loading'

export default defineComponent({
  setup () {
    const { startLoading, stopLoading, innerLoading } = useLoading()

    // ...
    
    return { startLoading, stopLoading, innerLoading, ... }
  }
})

My question:

// How can I define the loading prop inside the setup() function?
props: {
  loading: {
    type: Boolean,
    default: false
  },
},

Of course I can define my component like this:

import 'useLoading' from 'src/composables/loading'

export default defineComponent({
  props: {
    loading: {
      type: Boolean,
      default: false
    },
  },
  setup () {
    const { startLoading, stopLoading, innerLoading } = useLoading();
  }
})

But imagine, I have 20 components using this mixin/composable. So I want to define that loading prop only ONCE (like I did in mixin).

Is there a way how to do it with composition API?

7
  • @mspiderv props can't be declared inside setup(). Since setup() itself receives the props values as its second argument, props would have to be declared before setup() is even invoked. Currently, the only way to declare props is using the Options API. Commented Mar 13, 2021 at 6:24
  • How did you finally implement this? How did you get access to loading prop in the watcher within the composable? Commented Jan 2, 2022 at 20:26
  • @Donkarnash I implement it by creating two functions: useLoading and withLoading. The useLoading contains the whole logic and withLoading returns props exactly like @Daniel said (stackoverflow.com/a/66604160/2987610). I don't use watcher in my new implementation, but't it seems to me like you are asking something else. Commented Jan 3, 2022 at 21:08
  • Thanks for response. in useLoading where you have all the logic how do you access the props of component which is using the useLoading composable? I can understand that in the consuming component you include ...withLoading() to declare the prop loading but then how do you get access to the prop in useLoading? Commented Jan 3, 2022 at 23:34
  • 1
    @Donkarnash Like this: export function useLoading (props, ctx) { props.loading; } But of course, you need to pass props and ctx to useLoading like this: setup (props, ctx) { const { ... } = useLoading(props, ctx); return [ ... ] } Another syntax is this: setup(props, ctx) { return [ ...useLoading(props, ctx) ] } or this: setup: useLoading Commented Jan 5, 2022 at 10:50

2 Answers 2

12

you may be able to do something like this

import {withProps, useLoading} from "src/composables/loading";

export default defineComponent({
  props: {
    ...withProps()
  },
  setup () {
    const { startLoading, stopLoading, innerLoading } = useLoading();
  }
})

where withProps is a function that would have your definitions

export const withProps = () => ({
  loading: {
    type: Boolean,
    default: false
  },
})

of course it doesn't need to be a function, but in some cases it may be helpful and preemptively making it a function can make api consistent.

Sign up to request clarification or add additional context in comments.

Comments

4

Define an Object called loadingProps in separate file called makeLoadingProps:

export const loadingProps = {
    loading: {
        type: Boolean,
        default: false
    }
}

then import it inside your component defined using the script setup syntax:

<script setup lang="ts">

import {defineProps} from 'vue'
import { loadingProps } from 'src/composables/makeLoadingProps';

const props = defineProps({
               ...loadingProps,
                //other props
             })

const { startLoading, stopLoading, innerLoading } = useLoading(props)

</script>

3 Comments

MIXINS ARE NO LONGER RECOMENDED! In Vue 2, mixins were the primary mechanism for creating reusable chunks of component logic. While mixins continue to be supported in Vue 3, Composition API is now the preferred approach for code reuse between components. (From the Vue manual)
Thank you for this reminder, I'll try to update my answer later
What would be the return type of loadingProps in TypeScript?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.