简体   繁体   English

当商店中的状态更新时,Vue组件不会更新

[英]Vue component doesn't update when state in store is updated

I'm trying to do a simple todo list in Vue but I want to abstract everything out and use a dummy REST API so I can start to get used to production-level projects in Vue and it's all making my head spin. 我试图在Vue中创建一个简单的待办事项清单,但我想抽象出所有内容并使用虚拟的REST API,这样我就可以开始习惯于Vue中的生产级项目,这一切都使我很头疼。 GET, PUT, and POST requests seem to be working, but I can't figure out why the list of todos doesn't update automatically when I do a successful POST request to the back end. GET,PUT和POST请求似乎可以正常工作,但是当我对后端执行成功的POST请求时,我无法弄清楚为什么待办事项列表不会自动更新。

I've got a TodoList component that loops through a todosFiltered() computed property to show the todos. 我有一个TodoList组件,该组件遍历todosFiltered()计算属性以显示todosFiltered() The computed property refers back to the getter todosFiltered in the Vuex store. 计算出的属性返回到Vuex存储中的getter todosFiltered I also use the created() lifecycle hook here to dispatch an action in the store that makes the initial GET request and then populates an array called todos in the store when the page is first loaded. 我还使用此处的created()生命周期钩子在商店中调度一个操作,该操作发出初始GET请求,然后在首次加载页面时在商店中填充一个称为todos的数组。 The getter todosFiltered in the store returns state.todos , so I assumed that when my component re-renders, it would have the new todos array from the state grabbed from todosFiltered , only that's not happening. 存储todosFiltered中的getter todosFiltered返回state.todos ,因此我假设当我的组件重新渲染时,它将具有从todosFiltered抓取的状态中的新todos数组,只有这种情况没有发生。 What am I missing here? 我在这里想念什么? Any advice would be greatly appreciated. 任何建议将不胜感激。

TodoList.vue TodoList.vue

(I know I'll have to work out a solution for the ids, it's on my list :p) (我知道我必须为id设计一个解决方案,它在我的列表上:p)

<template>
    <div class="container">
        <input v-model="newTodo" type="text" placeholder="What must be done?" class="todo-input" @keyup.enter="addTodo">
        <transition-group name="fade" enter-active-class="animated zoomIn" leave-active-class="animated zoomOut">
            <todo-item v-for="todo in todosFiltered" :key="todo.id" :checkAll="!anyRemaining" :todo="todo"></todo-item>
        </transition-group>
        <div class="extra-container">
            <todos-filtered></todos-filtered>
        </div>
    </div>
</template>

<script>
import TodosFiltered from './TodosFiltered'
import TodoItem from './TodoItem'

export default {
  name: 'todolist',
  components: {
    TodosFiltered,
    TodoItem
  },
  data() {
    return {
        beforeEditCache: '',
        newTodo: '',
        idForTodo: 10,
    }
  },
  // Methods
  methods: {
    addTodo() {
        if (this.newTodo.trim().length == 0) {
            return
        }
        this.$store.dispatch('addTodo', {
            id: this.idForTodo,
            title: this.newTodo,
            completed: false
        })
        this.newTodo = ''
        this.idForTodo++
    }
  },
  computed: {
    todosFiltered() {
        return this.$store.getters.todosFiltered
    },
  },
  created() {
    this.$store.dispatch('loadTodos')
  },
}
</script>

store.js store.js

export const store = new Vuex.Store({
    state: {
        filter: 'all',
        todos: []
    },
    getters: {
        todosFiltered(state) {
            if (state.filter == 'all') {
                return state.todos
            } else if (state.filter == 'active') {
                return state.todos.filter(todo => !todo.completed)
            } else if (state.filter == 'completed') {
                return state.todos.filter(todo => todo.completed)
            }
            return state.todos
        },
        showClearCompleted(state) {
            return state.todos.filter(todo => todo.completed).length > 0
        }
    },
    mutations: {
        addTodo(state, todo) {
            state.todos.push(todo)
        },
        setTodos(state, todos) {
            state.todos = todos
        },
    },
    actions: {
        loadTodos(context) {
            axios.get('http://localhost:3000/todos')
            .then(r => r.data)
            .then(todos => {
                context.commit('setTodos', todos)
            })
        },
        updateTodo(context, todo) {
            axios.put('http://localhost:3000/todos/' + todo.id, {
                "id": todo.id,
                "title": todo.title,
                "completed": todo.completed
            })
        },
        addTodo(context, todo) {
            axios.post('http://localhost:3000/todos', {
                "id": todo.id,
                "title": todo.title,
                "completed": todo.completed
            })
            .then(todo => {
                context.commit('addTodo', todo)
            })
        },
    }
})

EDIT: Here's what's going on in Vue Dev Tools when I add a todo -- todos in the store's state gets updated immediately, and the todosFiltered computed property in the TodoList component ALSO reflects that -- but the new todo doesn't appear in the list! 编辑:这是发生了什么事情在Vue的开发工具,当我加入待办事项- todos在店内的状态将立即被更新,并且todosFiltered在计算财产TodoList分量也反映了-但新的待办事项不会出现在列出! Strange. 奇怪。

The problem is using a $store.getter on the v-for loop. 问题是在v-for循环上使用了$ store.getter。 Try the following: 请尝试以下操作:

  1. Set your computed to 设置您的计算为

    todos() { return this.$store.todos; todos(){返回this。$ store.todos; } }

  2. Change your v-for loop to use todo in todos 更改v-for循环以在待办事项中使用待办事项

  3. Add a v-if condition to the loop like v-if="filtered(todo)" 将v-if条件添加到循环中,例如v-if =“ filtered(todo)”
  4. Create a new method called filtered (or whatever you prefer), and add your "filteredTodos" logic there, returning true/false as needed 创建一个称为filtered(或您喜欢的任何一种)的新方法,并在其中添加“ filteredTodos”逻辑,根据需要返回true / false
  5. If you need to share this code, you can always use a mixin and share it between your components 如果您需要共享此代码,则始终可以使用mixin并在组件之间共享

Hope this works for you 希望这对你有用

A way to solve this can be to create what I like to call a refresh() method. 解决此问题的一种方法可以是创建我喜欢调用的refresh()方法。

Basically, you will have a local list of todos in your data() method, the refresh() method will load all the todos from the store into the local todos list, every time you do an action, such as creating, deleting, or updating, you would call the refresh method to re-load the list for you. 基本上,你将有一个本地列表todosdata()方法中, refresh()方法将所有负载todos从商店到本地todos列表,每次你做一个动作时,如创建,删除或更新后,您将调用refresh方法为您重新加载列表。

So, in your TodoList.vue : 因此,在您的TodoList.vue

<template>
    <todo-item v-for="todo in todosFiltered" :key="todo.id"></todo-item>
</template>

<script>
export default {
    data() {
        return {
            // Where we store the local list of Todos 
            // so the component will react when we do something to it
            todosFiltered: []
        }
    },
    methods {
        refresh() {
            // Get the todo list from the store.
            // which in turn will trigger a change event so the component
            // react to what we did.
            this.todosFiltered = this.$store.getters.todosFiltered;
        },
        addTodo() {
            this.$store.dispatch('addTodo').then(() => {
                // Refresh the list after adding a new Todo
                this.refresh();
            })
        },
        updateTodo() {
            this.$store.dispatch('updateTodo').then(() => {
                // Refresh the list after updating a Todo
                this.refresh();
            })
        },
        deleteTodo() {
            this.$store.dispatch('deleteTodo').then(() => {
                // Refresh the list after deleting a Todo
                this.refresh();
            })
        }
    },
    created() {
        this.$store.dispatch('loadTodos').then( () => {
            // Refresh the list when first loaded after the Todos been saved in Vuex
            this.refresh();
        })
    }
}
</script>

Don't actually delete what you already have and replace it with this, just apply what's here to your code. 实际上,不要删除已有的东西,而要用它替换,只需将此处的内容应用于代码即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM