'Extract modelValue logic to composable

I'm transitioning from Vue 2 to Vue 3 and I'm having trouble with composables. I have a bunch of components that inherits modelValue. So, for every component that uses modelValue I'm writing this code (example with a radio input component):

<script setup>
import { computed } from 'vue'

const emit = defineEmits(['update:modelValue'])
const props = defineProps({
  modelValue: {
    type: [String, null],
    required: true
  }
})

const computedValue = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})
</script>

<template>
  <label class="radio">
    <input
      v-model="computedValue"
      v-bind="$attrs"
      type="radio"
    >
    <slot />
  </label>
</template>

Is there a way to reuse the code for the modelValue?



Solution 1:[1]

I've just done this while I'm playing with Nuxt v3.

You can create a composable like this:

import { computed } from 'vue'

export function useModel(props, emit) { 
  return computed({ 
    get: () => props.modelValue, 
    set: (value) => emit('update:modelValue', value) 
  })
}
<template>
  <input type="text" v-model="value" />
</template>

<script setup lang="ts">
  const props = defineProps({
    modelValue: String,
  })

  const emit = defineEmits(['update:modelValue'])

  const value = useModel(props, emit)
</script>

Solution 2:[2]

For completion of @BghinC's perfect answer here the fully typed version:

Composable

File: @/composables/useModelValue.ts

import {computed} from 'vue'

export default function useModelValue<T>(
  props: {
    modelValue: T
    [key: string]: unknown
  },
  emit: (event: 'update:modelValue', ...args: unknown[]) => void
) {
  return computed({
    get: () => props.modelValue,
    set: (value: T) => emit('update:modelValue', value),
  })
}

Usage

<script setup lang="ts">
import useModelValue from '@/composables/useModelValue'

const props = defineProps<{
  modelValue: Dog
}>()

const emit = defineEmits(['update:modelValue'])

const dog = useModelValue<Dog>(props, emit)
</script>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Pascal