Search code examples
vuejs3pinia

Provide() reactive Pinia properties to child components


Goal

Is it possible to provide a Pinia store property while preserving its reactivity in the children? I've tried wrapping it with computed and function. I'm leaning away from props on this one to reduce prop drilling.

Using dispatchStore directly in each child is an option, but I am hoping to centralize things a bit.

Target Code

import { DivisionsProvider } from '../symbols'
const dispatchStore = useDispatchStore()

provide(DivisionsProvider, {
    jobsData,
    // Loses reactivity in child
    selectedDivisionId: dispatchStore.selectedDivisionId,
    // Loses reactivity in child
    selectedDivisionStatus: dispatchStore.selectedDivisionStatus,
    onSelectDivisionId,
    onSelectDivisionStatus,
    getStatusesWithJobCounts
})

Environment

  • pinia: ^2.0.32
  • vue: ^3.4.15

Solution

  • You could try toRef():

    import {toRef} from 'vue';
    import { DivisionsProvider } from '../symbols'
    const dispatchStore = useDispatchStore()
    
    provide(DivisionsProvider, {
        jobsData,
        // Loses reactivity in child
        selectedDivisionId: toRef(dispatchStore, 'selectedDivisionId'),
        // Loses reactivity in child
        selectedDivisionStatus: toRef(dispatchStore, 'selectedDivisionStatus'),
        onSelectDivisionId,
        onSelectDivisionStatus,
        getStatusesWithJobCounts
    });
    

    But I would prefer a getter/setter:

    
    import { DivisionsProvider } from '../symbols'
    const dispatchStore = useDispatchStore()
    
    provide(DivisionsProvider, {
        jobsData,
        // Loses reactivity in child
        get selectedDivisionId(){
          return dispatchStore.selectedDivisionId;
        }
        set selectedDivisionId(val){
          dispatchStore.selectedDivisionId = val
        }
        // Loses reactivity in child
        get selectedDivisionStatus(){
          return dispatchStore.selectedDivisionStatus;
        }
        set selectedDivisionStatus(val){
          dispatchStore.selectedDivisionStatus= val
        }
        onSelectDivisionId,
        onSelectDivisionStatus,
        getStatusesWithJobCounts
    })
    

    As you see it's a pattern so I would have a utility:

    
    import { DivisionsProvider } from '../symbols'
    const dispatchStore = useDispatchStore()
    
    provide(DivisionsProvider,
        // implement yourself with Object.defineProperty
        reactiveCopy({
        jobsData,
        onSelectDivisionId,
        onSelectDivisionStatus,
        getStatusesWithJobCounts
      }, dispatchStore, 'selectedDivisionId', 'selectedDivisionId')
    )