How to make a pinia work with nested objects in vue3
Asked Answered
D

3

11

How can I get a reactive component that updates nested properties:

I have a pinia store defined as follows

import { defineStore } from "pinia"
export const useStore = defineStore({
    id: "poc",
    state: () => ({ str: "", nested: { obj: "" } }),
    persist: {
        enabled: true,
        strategies: [{ storage: localStorage }],
    },
})

and the following vue3 component

<script lang="ts">
import { ref } from "vue"
import { storeToRefs } from "pinia"
import { useStore } from "./store"
export default {
    setup() {
        const store = useStore()
        const example = storeToRefs(store)

        const mStr = ref(example.str)
        const mObj = ref(example.nested.value.obj) // <--- this is where I believe the problem is
        store.str = mStr.value
        store.nested.obj = mObj.value

        return { mObj, mStr, store }
    },
}
</script>

<template>
    <h1>PoC</h1>
    <input v-model="mObj" placeholder="obj" />
    <input v-model="mStr" placeholder="str" />
</template>

when I update the str field it works as expected, but for nested object it doesn't. My suspicion is that I lose reactivity when calling nested.value, that said - I don't know how to make it reactive.

Diverticulum answered 4/6, 2022 at 12:17 Comment(0)
D
1

a little bit more digging and https://github.com/vuejs/pinia/discussions/854 finally gave me enough to come up with a (much more elegant) solution on my own.

<script lang="ts">
import { useStore } from "./store"
export default {
    setup() {
        const store = useStore()
        return { store }
    },
}
</script>

<template>
    <h1>test</h1>
    <input v-model="store.str" placeholder="obj" />
    <input v-model="store.nested.obj" placeholder="str" />
</template>
Diverticulum answered 4/6, 2022 at 14:21 Comment(0)
B
2

FOR PINIA: destructuring the state checkout :storeToRefs()

In order to extract properties from the store while keeping its reactivity, you need to use storeToRefs(). It will create refs for every reactive property. This is useful when you are only using state from the store but not calling any action. Note you can destructure actions directly from the store as they are bound to the store itself too

<script>
import { useStore } from "./store"
import { storeToRefs } from 'pinia' // NOTE this

export default {
    setup() {
        const store = useStore()
const {str, nested } = storeToRefs(store)
        return { str, nested }
    },
}
</script>

<template>
    <h1>test</h1>
    <input v-model="str" placeholder="obj" />
    <input v-model="nested.obj" placeholder="str" />
</template>
Begum answered 30/9, 2022 at 10:18 Comment(1)
storeToRefs return ref from vue To access from code nested.value.objCompendium
D
1

a little bit more digging and https://github.com/vuejs/pinia/discussions/854 finally gave me enough to come up with a (much more elegant) solution on my own.

<script lang="ts">
import { useStore } from "./store"
export default {
    setup() {
        const store = useStore()
        return { store }
    },
}
</script>

<template>
    <h1>test</h1>
    <input v-model="store.str" placeholder="obj" />
    <input v-model="store.nested.obj" placeholder="str" />
</template>
Diverticulum answered 4/6, 2022 at 14:21 Comment(0)
F
0

There is actually another way to achieve this. You can simply user "toRef()" or "toRefs()" functions to make nested objects reactive. In your specific case it would be something like this:

const { str, nested } = storeToRefs(store)
const { object } = toRef(nested.value, 'object')

Hope that helps someone!

Flotsam answered 6/3 at 21:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.