skip to Main Content

In React I was able to update a Boolean value from the child component using a useState in a prop to pass a new value back and forward, however in Vue I’m struggling to do similar.

I have been able to pass the Boolean value to the child component by doing this:

Parent:

<template>
  <button @click="viewProduct()"
  <ChildComponent v-if="showProduct === true" :showProduct = showProduct />
</template>

<script setup>
  import { ref, defineProps } from 'vue'
  import ChildComponent from './ChildComponent.vue'
  
  let showProduct = ref(false)
  const viewProduct = (e) => {
      showProduct.value = true
  }
</script>

and I can see the value passed through to the child as a prop and I can use it in the child component.

However if I try to change the variable in the child component with something like this:

Child:

<template>
  <div class="close" @click="hidePDP()">X</div>
</template>

<script setup>
    import { defineProps, computed } from 'vue'
    
    const props = defineProps({
        showProduct: Boolean,
    });
    
    const hidePDP = computed( () => {       
        return props.showProduct = false
    });
</script>

I get these errors and I’m guessing props even when they’ve been a ref() value still don’t like you mutating them.

60:16  error  Unexpected mutation of "showProduct" prop    vue/no-mutating-props
60:16  error  Unexpected side effect in computed function

However I haven’t been able to successfully find a way to update the Boolean value in the child (reverse the Boolean value) so the parent can remove the ChildComponent element from the render when a close button is clicked in the Child Component itself.

I hope that makes sense, I’ve seen a few questions that talk about getting help for the reverse of sending prop data to the child from the parent (which I’ve already got working) like props value not sending the updated value from parent to child component in vue and Creating local copy of passed props in child component in vue.js?, but not an exact answer to the above.

2

Answers


  1. I see 2 major mistakes in the code you provide.
    First, you’re trying to mutate the prop, and the second you’re doing anything but counting in computed.

    Best practise in vue is to send prop to a ChildComponent and listen emits from it and mutate in the parent component.

    Parent component:

    <template>
      <button @click="viewProduct()">click me</button>
      <ChildComponent v-if="showProduct" @close="onHidePDP" />
    </template>
    
    <script setup>
      import { ref, defineProps } from 'vue'
      import ChildComponent from './ChildComponent.vue'
      
      let showProduct = ref(false)
      const viewProduct = (e) => {
          showProduct.value = true
      }
      const onHidePDP = (e) => {
          showProduct.value = false; //we always pass false. So I didn't use event
      }
    </script>
    

    Child:

    <template>
      <div class="close" @click="close">X</div>
    </template>
    
    <script setup>
        const emit = defineEmits(['close']); // the same as $emit in the template. It can be written in the template `<div class="close" @click="$emit('close')">X</div>` without this setup functions
        function close () {
          emit('close')
        }
    </script>
    

    useful links:

    https://vuejs.org/guide/components/events.html#component-events

    https://vuejs.org/guide/essentials/computed.html#getters-should-be-side-effect-free

    If your component should be bind both ways look at v-model for components:
    https://vuejs.org/guide/components/v-model.html#component-v-model

    Login or Signup to reply.
  2. Just use v-model:

    Playground

    
    <script setup>
      import { ref } from 'vue'
      import ChildComponent from './Child.vue'
      
      let showProduct = ref(false)
      
    </script>
    
    <template>
      <button @click="showProduct = true">view product</button>
      <ChildComponent v-if="showProduct" v-model:show="showProduct" />
    </template>
    

    Child:

    <script setup>
        const show = defineModel('show');
    </script>
    
    <template>
      <div class="close" @click="show = false">X</div>
    </template>
    

    I would even prefer to move v-if into the child:

    Playground

    Or even move the show button into the child… so no any v-model is needed.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search