Vue 3 组件失去响应性

huangapple go评论67阅读模式
英文:

Vue 3 Component losing reactivity

问题

我已经学习了Vue 3大约一个月,取得了相当大的进展,但无论我尝试了什么,都无法修复这个问题。我知道我失去了响应性,但我无法弄清楚如何做,这让我感到非常烦恼。我正在使用Composition API和带有简单Pinia存储的脚本设置。我在这里创建了一个GitHub存储库:https://github.com/thammer67/vue3-reactivity-problem

我有一个视图(ProjectsView.vue),其中包含循环遍历Pinia存储数组的项目元素,使用v-for并将数组对象作为prop传递。ProjectsView.vue使用一个隐藏的表单组件(ProjectForm.vue),我用它来添加新项目。循环中的每个项目都是另一个组件(ProjectItem.vue),具有指向加载ProjectDetail.vue的路由的点击处理程序。ProjectDetail.vue有一个点击处理程序,也使用ProjectForm.vue来编辑项目。

一切都运作良好。我可以添加新项目,编辑项目,但当我编辑一个项目时,Pinia存储会更新(我可以在Vue Dev工具中看到),但UI不会更新,直到我返回到项目列表。我需要在保存后更新ProjectDetail.vue中的值。以下是相关文件。

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>

这些是您提供的代码的翻译。如果您需要更多帮助或有其他问题,请随时提出。

英文:

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:

&lt;script setup&gt;
import { useProjectStore } from &#39;../stores/ProjectStore&#39;
import { useRoute } from &#39;vue-router&#39;
import { ref } from &#39;vue&#39;
import ProjectForm from &#39;@/components/Form/ProjectForm.vue&#39;
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)=&gt; {
editing.value = id
showEditProject.value = true
}
&lt;/script&gt;
&lt;template&gt;
&lt;div class=&quot;main&quot;&gt;
&lt;div v-if=&quot;project&quot; :project=&quot;project&quot;&gt;
&lt;h2&gt;Project Details&lt;/h2&gt;
&lt;div&gt;
&lt;div class=&quot;project-name&quot;&gt;{{ project.project }}&lt;/div&gt; 
&lt;/div&gt;
&lt;div style=&quot;margin-top: 1em&quot;&gt;
&lt;button type=&quot;button&quot; @click=&quot;editProject(project.id)&quot;&gt;Edit&lt;/button&gt;
&lt;/div&gt;
&lt;ProjectForm
@hideForm=&quot;showEditProject=false&quot; 
:project=&quot;project&quot;
:editing=&quot;editing&quot;
:showAddEntry=&quot;showEditProject&quot; /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/template&gt;

ProjectForm.vue:

&lt;script setup&gt;
import { ref, toRef, reactive } from &quot;vue&quot;
import { useProjectStore } from &#39;@/stores/ProjectStore.js&#39;
import Input from &#39;./Input.vue&#39;
const projectStore = useProjectStore() 
const showAddType = ref(false)
//Capture &#39;showAddEntry&#39; prop from parent component
const props = defineProps([&#39;showAddEntry&#39;, &#39;editing&#39;, &#39;project&#39;])
//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:  &quot;&quot;
})
const saveProject = () =&gt; {
formState.invalid = false
if(projId.value) {
console.log(`Update existing project ${projId.value}`)
projectStore.updateProject({
id: projId.value,
project: projName.value
})
.then(()=&gt; {
console.log(&quot;save was successful!&quot;)
showAddType.value = false
formState.invalid = false
formState.errMsg = &quot;&quot;
emit(&#39;hideForm&#39;)
})
.catch(err=&gt;console.log(&quot;Error: &quot;, err))
} else {
console.log(`Create new project`)
//New Project
projectStore.createProject({
project: projName.value,
})
.then(()=&gt; {
showAddType.value = false
formState.invalid = false
formState.errMsg = &quot;&quot;
emit(&#39;hideForm&#39;)
})
}
}
const hideForm = ()=&gt; {
formState.invalid = false
showAddType.value=false
emit(&#39;hideForm&#39;)
}
//Define emit event up to the parent that hides the form
const emit = defineEmits([&#39;hideForm&#39;])
&lt;/script&gt;
&lt;template&gt;
&lt;div class=&quot;addform&quot; :class=&quot;{ show: props.showAddEntry }&quot;&gt;
&lt;h1 v-if=&quot;editing&quot; class=&quot;title&quot;&gt;Edit Project&lt;/h1&gt;
&lt;h1 v-else class=&quot;title&quot;&gt;Add New Project&lt;/h1&gt;
&lt;div class=&quot;input-wrap&quot; :class=&quot;{ &#39;input-err&#39; : formState.invalid }&quot;&gt;
&lt;Input 
@input=&quot;projName = $event.target.value&quot;
type=&quot;text&quot; 
placeholder=&quot;Enter project name&quot; 
:value=&quot;projName&quot;
/&gt;
&lt;div class=&quot;entry-submit&quot;&gt;
&lt;button v-if=&quot;editing&quot; @click=&quot;saveProject&quot;&gt;Save&lt;/button&gt;
&lt;button v-else @click=&quot;saveProject&quot;&gt;Create Project&lt;/button&gt;
&lt;button @click=&quot;hideForm&quot;&gt;Cancel&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p v-show=&quot;formState.invalid&quot; class=&quot;err-msg&quot;&gt;{{ formState.errMsg }}&lt;/p&gt;
&lt;/div&gt;
&lt;/template&gt;

答案1

得分: 1

ProjectDetails.vue 中的 project 不知道在存储中对它所做的更改。如果你用 computed() 包裹它,它就会知道。

import { computed } from 'vue'

const project = computed(() => projectStore.getProjectById(id))
英文:

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 &#39;vue&#39;

const project = computed(() =&gt; projectStore.getProjectById(id))

huangapple
  • 本文由 发表于 2023年6月1日 23:12:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/76383370.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定