title: How to Watch Props Change with Vue Composition API / Vue 3?
body: <p>While <a href="https://vue-composition-api-rfc.netlify.com/api.html#watch" rel="noreferrer">Vue Composition API RFC Reference site</a> has many advanced use scenarios with the <code>watch</code> module, there is no examples on <strong>how to watch component props</strong>?</p>
  <p>Neither is it mentioned in <a href="https://vue-composition-api-rfc.netlify.com/#api-introduction" rel="noreferrer">Vue Composition API RFC's main page</a> or <a href="https://github.com/vuejs/composition-api#watch-api" rel="noreferrer">vuejs/composition-api in Github</a>.</p>
  <p>I've created a <a href="https://codesandbox.io/s/how-to-watch-props-with-vue-composition-api-ijno4" rel="noreferrer">Codesandbox</a> to elaborate this issue.</p>
  <pre class="lang-html prettyprint-override"><code>&lt;template&gt;
    &lt;div id=&quot;app&quot;&gt;
      &lt;img width=&quot;25%&quot; src=&quot;./assets/logo.png&quot;&gt;
      &lt;br&gt;
      &lt;p&gt;Prop watch demo with select input using v-model:&lt;/p&gt;
      &lt;PropWatchDemo :selected=&quot;testValue&quot;/&gt;
    &lt;/div&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
  import { createComponent, onMounted, ref } from &quot;@vue/composition-api&quot;;
  import PropWatchDemo from &quot;./components/PropWatchDemo.vue&quot;;
  
  export default createComponent({
    name: &quot;App&quot;,
    components: {
      PropWatchDemo
    },
    setup: (props, context) =&gt; {
      const testValue = ref(&quot;initial&quot;);
  
      onMounted(() =&gt; {
        setTimeout(() =&gt; {
          console.log(&quot;Changing input prop value after 3s delay&quot;);
          testValue.value = &quot;changed&quot;;
          // This value change does not trigger watchers?
        }, 3000);
      });
  
      return {
        testValue
      };
    }
  });
  &lt;/script&gt;
  </code></pre>
  <pre class="lang-html prettyprint-override"><code>&lt;template&gt;
    &lt;select v-model=&quot;selected&quot;&gt;
      &lt;option value=&quot;null&quot;&gt;null value&lt;/option&gt;
      &lt;option value&gt;Empty value&lt;/option&gt;
    &lt;/select&gt;
  &lt;/template&gt;
  
  &lt;script&gt;
  import { createComponent, watch } from &quot;@vue/composition-api&quot;;
  
  export default createComponent({
    name: &quot;MyInput&quot;,
    props: {
      selected: {
        type: [String, Number],
        required: true
      }
    },
    setup(props) {
      console.log(&quot;Setup props:&quot;, props);
  
      watch((first, second) =&gt; {
        console.log(&quot;Watch function called with args:&quot;, first, second);
        // First arg function registerCleanup, second is undefined
      });
  
      // watch(props, (first, second) =&gt; {
      //   console.log(&quot;Watch props function called with args:&quot;, first, second);
      //   // Logs error:
      //   // Failed watching path: &quot;[object Object]&quot; Watcher only accepts simple
      //   // dot-delimited paths. For full control, use a function instead.
      // })
  
      watch(props.selected, (first, second) =&gt; {
        console.log(
          &quot;Watch props.selected function called with args:&quot;,
          first,
          second
        );
        // Both props are undefined so its just a bare callback func to be run
      });
  
      return {};
    }
  });
  &lt;/script&gt;
  </code></pre>
  <p><strong>EDIT</strong>: Although my question and code example was initially with JavaScript, I'm actually using TypeScript. Tony Tom's first answer although working, lead to a type error. Which was solved by Michal Levý's answer. So I've tagged this question with <code>typescript</code> afterwards.</p>
  <p><strong>EDIT2</strong>: Here is my polished yet barebones version of the reactive wirings for this custom select component, on top of <code>&lt;b-form-select&gt;</code> from <code>bootstrap-vue</code> <em>(otherwise agnostic-implementation but this underlying component does emit @input and @change events both, based on whether change was made programmatically or by user interaction)</em>.</p>
  <pre class="lang-html prettyprint-override"><code>&lt;template&gt;
    &lt;b-form-select
      v-model=&quot;selected&quot;
      :options=&quot;{}&quot;
      @input=&quot;handleSelection('input', $event)&quot;
      @change=&quot;handleSelection('change', $event)&quot;
    /&gt;
  &lt;/template&gt;
  
  &lt;script lang=&quot;ts&quot;&gt;
  import {
    createComponent, SetupContext, Ref, ref, watch, computed,
  } from '@vue/composition-api';
  
  interface Props {
    value?: string | number | boolean;
  }
  
  export default createComponent({
    name: 'CustomSelect',
    props: {
      value: {
        type: [String, Number, Boolean],
        required: false, // Accepts null and undefined as well
      },
    },
    setup(props: Props, context: SetupContext) {
      // Create a Ref from prop, as two-way binding is allowed only with sync -modifier,
      // with passing prop in parent and explicitly emitting update event on child:
      // Ref: https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
      // Ref: https://medium.com/@jithilmt/vue-js-2-two-way-data-binding-in-parent-and-child-components-1cd271c501ba
      const selected: Ref&lt;Props['value']&gt; = ref(props.value);
  
      const handleSelection = function emitUpdate(type: 'input' | 'change', value: Props['value']) {
        // For sync -modifier where 'value' is the prop name
        context.emit('update:value', value);
        // For @input and/or @change event propagation
        // @input emitted by the select component when value changed &lt;programmatically&gt;
        // @change AND @input both emitted on &lt;user interaction&gt;
        context.emit(type, value);
      };
  
      // Watch prop value change and assign to value 'selected' Ref
      watch(() =&gt; props.value, (newValue: Props['value']) =&gt; {
        selected.value = newValue;
      });
  
      return {
        selected,
        handleSelection,
      };
    },
  });
  &lt;/script&gt;
  </code></pre>