Tracking down the root cause of unexpected reactivity in Vue 3 components

I am currently working on a fairly simple Vue-3 app, but experiencing an issue I’m not sure how to find the root cause of. In my app I have a view which is being rendered via the router. This view has some simple state (an object ref for storing the values of some text fields, and a boolean ref for toggling a modal on and off), and also makes use of a single state object from a pinia store.

The very first time I load the view I can edit the text fields which are bound to the object ref with ease, but the very first time I attempt to open the modal by clicking a button two things happen: The text fields are cleared, and the modal fails to open. After the button has been clicked the very first time, everything works the way I expect, and I can open and close the modal without disrupting the state of the view.

I have attempted adding watchers to all of the internal state refs so I can see if the values are changing, but I am not seeing those watchers triggered when the text fields are cleared after the first button click, and I’m at a loss for figuring out what might be causing this reactivity to occur.

For context, here is the code of the view (TemplateEditorView.vue):

<script setup>

import TemplateSelectorComponent from '../components/TemplateSelectorComponent.vue'
import useTemplate from '../composables/useTemplate'
import { computed, ref, watch } from 'vue'
import AddSectionModal from '../components/AddSectionModal.vue'
import { useTemplateStore } from '../stores/template'
import { storeToRefs } from 'pinia'

const templateStore = useTemplateStore()
const { templates } = storeToRefs(templateStore)
const { selectedTemplate, selectedTemplateKey, updateSelectedTemplateKey } = useTemplate()

const newTemplate = ref({
    id: '',
    name: '',
    templateText: '',
    sections: []
})
const showModal = ref(false)

const handleModalSubmit = function (section) {
    newTemplate.value.sections.push(section)
    showModal.value = false
}

const handleSave = function () {
    if (saveEnabled.value) {
        templates.value[newTemplate.value.id] = newTemplate.value
        clearForm()
    }
}

function clearForm() {
    newTemplate.value = {
        id: '',
        name: '',
        templateText: '',
        sections: []
    }
}

const handleDelete = function () {
    if (deleteEnabled.value) {
        delete templates.value[selectedTemplateKey.value]
        clearForm()
    }
}

const deleteEnabled = computed(() => {
    return Object.hasOwn(templates.value, selectedTemplateKey.value)
})

const saveEnabled = computed(() => {
    return newTemplate.value.id && newTemplate.value.name && newTemplate.value.templateText
})

watch(selectedTemplate, async () => {
    newTemplate.value = selectedTemplate.value
})
</script>

<template>
    <div id="template-editor-view-content" class="row p-1 pt-4">
        <div class="col">
            <div class="row">
                <div class="col"><h1>Template Editor</h1></div>
            </div>
            <div class="row">
                <div id="options-column" class="col-4 primary-bordered vh-85">
                    <div class="row pt-3">
                        <div class="col"><h2 class="text-center">Template Options</h2></div>
                    </div>
                    <TemplateSelectorComponent @selected-template-changed="updateSelectedTemplateKey" />
                    <div class="row">
                        <div class="col">
                            <h3 class="text-center">Global Variables</h3>
                        </div>
                    </div>
                    <div class="row">
                        <div class="col">
                            <div id="globalVarsAccordion" class="accordion">
                                <div class="accordion-item">
                                    <h2 id="companyNameAccordionHeader" class="accordion-header">
                                        <button
                                            class="accordion-button"
                                            type="button"
                                            data-bs-toggle="collapse"
                                            data-bs-target="#companyNameAccordionBody"
                                            aria-expanded="false"
                                            aria-controls="companyNameAccordionBody"
                                        >
                                            Company Name
                                        </button>
                                    </h2>
                                    <div
                                        id="companyNameAccordionBody"
                                        class="accordion-collapse collapse"
                                        aria-labelledby="companyNameAccordionHeader"
                                        data-bs-parent="#globalVarsAccordion"
                                    >
                                        <div class="accordion-body">
                                            <ul class="list-group list-group-horizontal">
                                                <li class="list-group-item">isSelected</li>
                                                <li v-pre class="list-group-item">{{ companyName.isSelected }}</li>
                                            </ul>
                                            <ul class="pt-2 list-group list-group-horizontal">
                                                <li class="list-group-item">value</li>
                                                <li v-pre class="list-group-item">{{ companyName.value }}</li>
                                            </ul>
                                        </div>
                                    </div>
                                </div>
                                <div class="accordion-item">
                                    <h2 id="jobTitleAccordionHeader" class="accordion-header">
                                        <button
                                            class="accordion-button"
                                            type="button"
                                            data-bs-toggle="collapse"
                                            data-bs-target="#jobTitleAccordionBody"
                                            aria-expanded="false"
                                            aria-controls="jobTitleAccordionBody"
                                        >
                                            Job Title
                                        </button>
                                    </h2>
                                    <div
                                        id="jobTitleAccordionBody"
                                        class="accordion-collapse collapse"
                                        aria-labelledby="jobTitleAccordionHeader"
                                        data-bs-parent="#globalVarsAccordion"
                                    >
                                        <div class="accordion-body">
                                            <ul class="list-group list-group-horizontal">
                                                <li class="list-group-item">isSelected</li>
                                                <li v-pre class="list-group-item">{{ jobTitle.isSelected }}</li>
                                            </ul>
                                            <ul class="pt-2 list-group list-group-horizontal">
                                                <li class="list-group-item">value</li>
                                                <li v-pre class="list-group-item">{{ jobTitle.value }}</li>
                                            </ul>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <template v-if="newTemplate.sections">
                        <div class="row">
                            <div class="col">
                                <h3 class="text-center">Template Variables</h3>
                            </div>
                        </div>
                        <div class="row">
                            <div class="col">
                                <div id="templateVarsAccordion" class="accordion">
                                    <div
                                        v-for="section in newTemplate.sections"
                                        :key="section.key"
                                        class="accordion-item"
                                    >
                                        <h2 id="`${section.key}AccordionHeader`" class="accordion-header">
                                            <button
                                                class="accordion-button"
                                                type="button"
                                                data-bs-toggle="collapse"
                                                data-bs-target="#`${section.key}AccordionBody`"
                                                aria-expanded="false"
                                                aria-controls="`${section.key}AccordionBody`"
                                            >
                                                {{ section.label }}
                                            </button>
                                        </h2>
                                        <div
                                            id="`${section.key}AccordionBody`"
                                            class="accordion-collapse collapse"
                                            aria-labelledby="`${section.key}AccordionHeader`"
                                            data-bs-parent="#templateVarsAccordion"
                                        >
                                            <div class="accordion-body">
                                                <ul class="list-group list-group-horizontal">
                                                    <li class="list-group-item">isSelected</li>
                                                    <li class="list-group-item">
                                                        <span v-pre>{{</span> {{ section.key }}.isSelected <span v-pre>}}</span>
                                                    </li>
                                                </ul>
                                                <ul class="pt-2 list-group list-group-horizontal">
                                                    <li class="list-group-item">value</li>
                                                    <li class="list-group-item">
                                                        <span v-pre>{{</span> {{ section.key }}.value <span v-pre>}}</span>
                                                    </li>
                                                </ul>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </template>
                </div>
                <div id="working-area" class="col vh-85">
                    <form class="row gx-3 align-items-center pb-3 pt-2">
                        <div class="col-auto">
                            <label for="templateKeyInput" class="col-form-label-lg">Template ID</label>
                        </div>
                        <div class="col">
                            <input
                                id="templateKeyInput"
                                v-model="newTemplate.id"
                                type="text"
                                class="form-control form-control-lg"
                            />
                        </div>
                        <div class="col-auto">
                            <label for="templateNameInput" class="col-form-label-lg">Template Name</label>
                        </div>
                        <div class="col">
                            <input
                                id="templateNameInput"
                                v-model="newTemplate.name"
                                type="text"
                                class="form-control form-control-lg"
                            />
                        </div>
                    </form>
                    <form class="row text-center justify-content-center">
                        <div class="col-2">
                            <button class="btn btn-secondary" @click="showModal = true">Add Section</button>
                        </div>
                        <div class="col-2">
                            <button class="btn btn-success" :disabled="!saveEnabled" @click="handleSave">
                                Save Template
                            </button>
                        </div>
                        <div class="col-2">
                            <button class="btn btn-danger" :disabled="!deleteEnabled" @click="handleDelete">
                                Delete Template
                            </button>
                        </div>
                    </form>
                    <div class="row pt-3">
                        <div class="col">
                            <textarea
                                id="templateTextArea"
                                v-model="newTemplate.templateText"
                                class="form-control"
                            ></textarea>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <AddSectionModal :show="showModal" @close="showModal = false" @submit="handleModalSubmit" />
    </div>
</template>

<style scoped>
// Some css
</style>

The modal which is being created is defined as follows (AddSectionModal.vue):

<script setup>
import GenericModal from './GenericModal.vue'
import { computed, ref } from 'vue'

defineProps({
    show: Boolean
})
const emit = defineEmits(['close', 'submit'])

const section = ref({
    key: '',
    label: '',
    text: '',
    isSelected: false
})

function clearState() {
    section.value = {
        key: '',
        label: '',
        text: '',
        isSelected: false
    }
}

const submitEnabled = computed(() => {
    return section.value.key && section.value.text && section.value.label
})

const handleClose = function () {
    clearState()
    emit('close')
}

const handleSubmit = function () {
    if (submitEnabled.value) {
        const value = section.value
        clearState()
        emit('submit', value)
    }
}
</script>

<template>
    <Teleport to="body">
        <GenericModal :show="show">
            <template #header>
                <h5 class="mx-auto">Add New Section</h5>
            </template>

            <template #body>
                <div class="container">
                    <div class="row gx-3 align-items-center">
                        <div class="col-3">
                            <label for="sectionKey" class="col-form-label-lg">Section Key</label>
                        </div>
                        <div class="col">
                            <input
                                v-model="section.key"
                                name="sectionKey"
                                type="text"
                                class="form-control form-control-lg"
                            />
                        </div>
                    </div>
                    <div class="row pt-2 gx-3 align-items-center">
                        <div class="col-3">
                            <label for="sectionLabel" class="col-form-label-lg">Section Label</label>
                        </div>
                        <div class="col">
                            <input
                                v-model="section.label"
                                name="sectionLabel"
                                type="text"
                                class="form-control form-control-lg"
                            />
                        </div>
                    </div>
                    <div class="row pt-2 gx-3 align-items-center">
                        <div class="col-3">
                            <label for="sectionText" class="col-form-label-lg">Section Text</label>
                        </div>
                        <div class="col">
                            <input
                                v-model="section.text"
                                name="sectionText"
                                type="text"
                                class="form-control form-control-lg"
                            />
                        </div>
                    </div>
                </div>
            </template>

            <template #footer>
                <div class="row gx-1 float-end">
                    <div class="col">
                        <button class="btn btn-danger" @click="handleClose">Cancel</button>
                    </div>
                    <div class="col">
                        <button class="btn btn-success" :disabled="!submitEnabled" @click="handleSubmit">
                            Submit
                        </button>
                    </div>
                </div>
            </template>
        </GenericModal>
    </Teleport>
</template>

And the GenericModal implementation is as follows (GenericModal.vue):

<template>
    <Transition name="modal">
        <div v-if="show" class="modal-mask">
            <div class="modal-container">
                <div class="modal-header">
                    <slot name="header">default header</slot>
                </div>

                <div class="modal-body">
                    <slot name="body">default body</slot>
                </div>

                <div class="modal-footer">
                    <slot name="footer">
                        default footer
                        <button class="modal-default-button" @click="$emit('close')">OK</button>
                    </slot>
                </div>
            </div>
        </div>
    </Transition>
</template>

<style>
// Some CSS
</style>

What strategies could I use to track down the source of the unexpected reactivity?