Home > Mobile >  Vue 3, Pinia - child component is not updated when the store is updated
Vue 3, Pinia - child component is not updated when the store is updated

Time:07-05

I have this Pinia store:

import { defineStore } from 'pinia'
import axiosClient from '@/axios'

export const useTableOrderStore = defineStore( 'tableOrders', {
    id : 'tableOrders',
    state: () => {
        return {
            tableOrders: []
        }
    },
    actions: {
        addToOrder(item, quantity)
        {
            const searchIndex = this.tableOrders.findIndex((order) => order.id == item.id);

            if(searchIndex !== -1)
            {
                this.tableOrders[searchIndex].quantity  = quantity
            }
            else
            {
                item.quantity = quantity
                this.tableOrders.push(item)

            }

        }
    }
})

Parent component:

<script setup>

import {ref} from "vue"
import {useTableOrderStore} from "@/store/tableOrder";
import CounterInput from "@/components/Inputs/Counter.vue"

const tableOrderStore = useTableOrderStore()

const props = defineProps(['product'])

let quantity = ref(1)

let addToOrder = (product) => {
  tableOrderStore.addToOrder(product, quantity.value)
  quantity.value = 1
}

</script>

<template>

    <div >

      <counter-input :quantity="quantity"
                     @quantity-event="(n) => quantity = n"></counter-input>

      <button   @click="addToOrder(product)">
        Add <font-awesome-icon icon="fas fa-receipt"  />
      </button>

    </div>

</template>

Child component:

<script setup>

import {ref} from "vue"

let props = defineProps({
  quantity: {
    type: Number,
    required: true
  }
})

let count = ref(props.quantity)

let increase = () => {
  count.value  

  emit('quantityEvent', count.value)
}

let decrease = () =>
{
  if(count.value === 1)
    return

  count.value--

  emit('quantityEvent', count.value)
}

const emit = defineEmits(['quantityEvent'])

</script>
<template>

    <span >
        <button type="button"
                
                data-type="minus"
                @click="decrease"
        >
          <font-awesome-icon icon="fas fa-minus-circle" />
        </button>
    </span>
    <input type="text"
           name="quantity"
           :value="count"
           
           min="1"
    >
    <span >
        <button type="button"
                
                data-type="plus"
                @click="increase"
        >
            <font-awesome-icon icon="fas fa-plus-circle" />
        </button>
    </span>
</template>

The first time method addToOrder is fired, the product is correctly added and child product renders is. The first issue here is that the quantity is set to 1, but it is not set in the child component. The second problem is with the quantity - first addToOrder is ok, and quantity is shown correctly, but if new quantity is added the Pinia store is updated, but it is not reflected in the component. What am I doing wrong here?

CodePudding user response:

I guess you run into an vuejs caveat.

 this.tableOrders[searchIndex].quantity  = quantity

Vue cannot detect the following changes to an array:

When you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue When you modify the length of the array, e.g. vm.items.length = newLength

You directly set an item.

Instead, you could use .splice() to replace your item:

let newItem = {
   ...this.tableOrders[searchIndex],
   quantity: this.tableOrders[searchIndex].quantity   quantity
};

//replace old item with new
this.tableOrders.splice(searchIndex, 1, newItem)

Here are the mutation methods that triggers an update:

push()
pop()
shift()
unshift()
splice()
sort()
reverse()

The first time you use addToOrders works because you used .push() witch is one of the mutations that triggers the re-render.

CodePudding user response:

First issue, i dont know how props.quantity is not set in the child component. Try console props.quantity right after you defined props if it displayed the result as you want then try to console it inside watch method. If props.quantity has changed but your child component is not update then your child component somehow not updated. You can try to force update it and here's how: https://medium.com/emblatech/ways-to-force-vue-to-re-render-a-component-df866fbacf47 (Using the key changing technique)

Second issue, i think this one @quantity-event="(n) => quantity = n" should be @quantity-event="(n) => { quantity = n }"

  • Related