Search code examples
vuejs3vue-composition-apispreadvue-reactivity

Vue 3 reactive object update with spread operator


I recently started to look at Vue composition API and I must have miss something because I find this "new" approach a real PITA!

Previously, I was able to use a simple spread operator to update my form data with an REST API call in the created() hook. I tried to transpose this in the composition API but I'm kind of stuck on the syntax change (and I know that the spread operator "kill" the reactivity).

I was able to make it work using the setup() {} approach like that (btw, this is an over simplify but working example of my original page):

<script>
import { reactive, toRefs } from 'vue'

export default {
    setup() {
        let profil = reactive({
            memberNo: '',
            details: {
                firstname: '',
                lastname: '',
                date: '',
                address: {
                    city: '',
                    street: '',
                    postalCode: ''
                }
            }
        })

        return {
            ...toRefs(profil)
        }
    },
    created() {
       // Call some API
        let dataFromApi = {
            memberNo: '1234',
            details: {
                firstname: 'Micheal',
                lastname: 'Jordan',
                date: '1967-01-21',
                address: {
                    city: 'Quebec',
                    street: 'No street',
                    postalCode: 'H0H 0H0'
                }
            }
        }
        this.profil = { ...this.profil, ...dataFromApi }
    }
}
</script>

<template>
    <p>
        {{ profil.memberNo }}
        {{ profil.details.firstname }}
    </p>
</template>

So with the toRefs() method it work. But if I want to go all in with the <script setup> tag, I can't find a way to make this work ?!?

This code don't work with the spread operator but is "ok" with individual assignment (in remark)

<script setup>
import { reactive, onBeforeMount, toRefs } from 'vue'

let profil = reactive({
    memberNo: '',
    details: {
        firstname: '',
        lastname: '',
        date: '',
        address: {
            city: '',
            street: '',
            postalCode: ''
        }
    }
})

onBeforeMount(() => {
  // Call some API
    let dataFromApi = reactive({
        memberNo: '1234',
        details: {
            firstname: 'Micheal',
            lastname: 'Jordan',
            date: '1967-01-21',
            address: {
                city: 'Quebec',
                street: 'No street',
                postalCode: 'H0H 0H0'
            }
        }
    })
  // Don't work
    profil = { ...toRefs(profil), ...toRefs(dataFromApi) }
  // ... but this work
    // profil.memberNo = dataFromApi.memberNo
    // profil.details.firstname = dataFromApi.details.firstname
})
</script>

Please, give me my faith back with composition API, otherwise I will stick to Options for the rest of this project! :-)


Solution

  • profil = { ...toRefs(profil), ...toRefs(dataFromApi) }
    

    This can't work because you are creating a literal non-reactive object by using the {} syntax.

    I think you can use Object.assign(profil, dataFromApi) since the ref to profil will be preserved and so it will keep reactivity.