0

I am desperately trying to watch a prop in Vue3 (3.2.31, Composition API):

<script lang="ts" setup>
import { toRef, watch } from 'vue'

const props = defineProps({
  'trigger-refresh': {
    type: String
  }
})
const triggerRefresh = toRef(props, 'trigger-refresh')
watch(triggerRefresh, (a, b) => console.log('props triggered refresh'))
</script>

I trigger the emission of trigger-refresh, it is passed to the component with the code above and I see triggerRefresh changing in DevTools→Vue.

So everything is fine up to the inside of the component except that the watch is not triggered (i.e. there is no message on the console).

I read the documentation for Watchers and responses to a question about watching props (one of the responses is almost identical to what I did) but I simply fail to understand why this does not work in my case.

WoJ
  • 27,165
  • 48
  • 180
  • 345
  • what about `watch(()=>triggerRefresh, (a, b) => console.log('props triggered refresh'))` – Boussadjra Brahim Mar 04 '22 at 15:23
  • @BoussadjraBrahim: I tried that too without success (more or less without clearly understanding why, but found out that the first parameter can be a `ref`) – WoJ Mar 04 '22 at 15:25
  • Try to use camelCase instead of kebab-case like `const props = defineProps({ 'triggerRefresh': { type: String } })` – Boussadjra Brahim Mar 04 '22 at 15:28
  • @BoussadjraBrahim: just tried it - no changes. Please note that the `triggerRefresh` ref extracted from `props` does change correctly (I see that in DevTools) – WoJ Mar 04 '22 at 15:32
  • As a side note for someone who would be reading this: my intent was to reset the component through an external event. I was passing the prop and intended to reset the component "from inside". I just remembered that this can be done by setting a `key` property on the component (whet it changes, the component is reset). This does not change my question but is good to know. – WoJ Mar 04 '22 at 15:40
  • @WoJ Then this is XY problem. It won't work as `triggerRefresh` set to true and needs to be unique token in order to trigger a watcher multiple times. Instead, `triggerRefresh` public method should be exposed by the component – Estus Flask Mar 04 '22 at 15:41
  • @EstusFlask: sorry but I think I did not understand your comment. Is it related to my question, or the comment about `key` I added? – WoJ Mar 04 '22 at 15:43
  • try to add `immediate` option `watch(triggerRefresh, (a, b) => console.log('props triggered refresh'),{immediate:true})` – Boussadjra Brahim Mar 04 '22 at 15:54
  • @BoussadjraBrahim: yes! it worked :) Thank you very much. If you would be willing to turn the comment into an answer I would gladly accept it. Thanks again! – WoJ Mar 04 '22 at 16:10
  • @WoJ To the one with key. It's a hack here (also was with key). A correct way is to expose a method – Estus Flask Mar 04 '22 at 16:14
  • @EstusFlask: Is this really a hack? In https://vuejs.org/api/built-in-special-attributes.html#key it is stated that *It can also be used to force replacement of an element/component instead of reusing it.* – WoJ Mar 04 '22 at 16:16
  • It *can*. But generally shouldn't if you have control over a component in use, it's inefficient and lacks expressiveness. It's acceptable for third-party comps that works the way they work – Estus Flask Mar 04 '22 at 16:28
  • @EstusFlask: thanks. I never saw public methods in components but I will look that up. Thanks! – WoJ Mar 04 '22 at 16:30
  • In composition API this is done with `context.expose`. – Estus Flask Mar 04 '22 at 16:30

2 Answers2

8

Try to add the immediate option in order to trigger the watch at the first prop change:

watch(triggerRefresh, (a, b) => console.log('props triggered refresh'), {
  immediate: true
})
WoJ
  • 27,165
  • 48
  • 180
  • 345
Boussadjra Brahim
  • 82,684
  • 19
  • 144
  • 164
0

Prop names are normalized to camel case. As it was stated in the question, it is triggerRefresh and not trigger-refresh that is seen updated. So it should be:

const triggerRefresh = toRef(props, 'triggerRefresh')

watch(triggerRefresh, ...)

Or just:

watch(() => props.triggerRefresh, ...)
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • 1
    Unfortunately even when changing the name from `trigger-refresh` to `triggerRefresh` the `watch` is not triggered. And in any case as I mentioned in a comment the `triggerRefresh` ref extracted from `props` **does** change correctly (I see that in DevTools) – WoJ Mar 04 '22 at 15:46
  • 1
    I see. Then it should work. If the problem is that you expected it to be triggered immediately like suggested in comments then that's it, but this isn't something that could be expected from 'refresh' action. – Estus Flask Mar 04 '22 at 16:32