Reputation: 311
I've been learning Vue 3 for the past month or so and have gotten quite far but I can't fix this no matter what I've tried. I know I'm losing reactivity but I can't figure out how and it's driving me nuts. I am using the Composition API and script setup with a simple Pinia store. I created a github repo for it here: https://github.com/thammer67/vue3-reactivity-problem
I have a view (ProjectsView.vue) of project elements that loops through a pinia store array of projects using v-for and passing the array object as a prop. ProjectsView.vue uses a hidden form component (ProjectForm.vue) that I use for adding new projects. Each project in the loop is another component (ProjectItem.vue) with a click handler to a route that loads ProjectDetail.vue. ProjectDetail.vue has a click handler that also uses ProjectForm.vue for editing the item.
Everything works great. I can add new projects, edit projects but when I edit a project the pinia store updates (I can see this in the Vue Dev tools) but the UI doesn't update untill I go back to the project list. I need to update the value in ProjectDetail.vue after saving. Here are the pertinent files.
ProjectDetail.vue:
<script setup>
import { useProjectStore } from '../stores/ProjectStore'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import ProjectForm from '@/components/Form/ProjectForm.vue'
const projectStore = useProjectStore()
const route = useRoute()
const id = route.params.id
const project = projectStore.getProjectById(id)
const showEditProject = ref(false)
const editing = ref(false)
const editProject = (id)=> {
editing.value = id
showEditProject.value = true
}
</script>
<template>
<div class="main">
<div v-if="project" :project="project">
<h2>Project Details</h2>
<div>
<div class="project-name">{{ project.project }}</div>
</div>
<div style="margin-top: 1em">
<button type="button" @click="editProject(project.id)">Edit</button>
</div>
<ProjectForm
@hideForm="showEditProject=false"
:project="project"
:editing="editing"
:showAddEntry="showEditProject" />
</div>
</div>
</template>
ProjectForm.vue:
<script setup>
import { ref, toRef, reactive } from "vue"
import { useProjectStore } from '@/stores/ProjectStore.js'
import Input from './Input.vue'
const projectStore = useProjectStore()
const showAddType = ref(false)
//Capture 'showAddEntry' prop from parent component
const props = defineProps(['showAddEntry', 'editing', 'project'])
//Copy prop values for the form
const projName = toRef(props.project.project)
const projId = toRef(props.project.id)
//new/edited values are stored on this reactive object
const formState = reactive({
invalid: false,
errMsg: ""
})
const saveProject = () => {
formState.invalid = false
if(projId.value) {
console.log(`Update existing project ${projId.value}`)
projectStore.updateProject({
id: projId.value,
project: projName.value
})
.then(()=> {
console.log("save was successful!")
showAddType.value = false
formState.invalid = false
formState.errMsg = ""
emit('hideForm')
})
.catch(err=>console.log("Error: ", err))
} else {
console.log(`Create new project`)
//New Project
projectStore.createProject({
project: projName.value,
})
.then(()=> {
showAddType.value = false
formState.invalid = false
formState.errMsg = ""
emit('hideForm')
})
}
}
const hideForm = ()=> {
formState.invalid = false
showAddType.value=false
emit('hideForm')
}
//Define emit event up to the parent that hides the form
const emit = defineEmits(['hideForm'])
</script>
<template>
<div class="addform" :class="{ show: props.showAddEntry }">
<h1 v-if="editing" class="title">Edit Project</h1>
<h1 v-else class="title">Add New Project</h1>
<div class="input-wrap" :class="{ 'input-err' : formState.invalid }">
<Input
@input="projName = $event.target.value"
type="text"
placeholder="Enter project name"
:value="projName"
/>
<div class="entry-submit">
<button v-if="editing" @click="saveProject">Save</button>
<button v-else @click="saveProject">Create Project</button>
<button @click="hideForm">Cancel</button>
</div>
</div>
<p v-show="formState.invalid" class="err-msg">{{ formState.errMsg }}</p>
</div>
</template>
Upvotes: 2
Views: 1461
Reputation: 14639
project
in ProjectDetails.vue is not aware of changes being made to it in the store. It will if you wrap it with computed()
import { computed } from 'vue'
const project = computed(() => projectStore.getProjectById(id))
Upvotes: 1