【问题标题】:Vue component doesn't update when state in store is updated更新 store 中的状态时,Vue 组件不更新
【发布时间】:2019-03-13 12:17:31
【问题描述】:

我正在尝试在 Vue 中创建一个简单的待办事项列表,但我想将所有内容抽象出来并使用一个虚拟 REST API,这样我就可以开始习惯 Vue 中的生产级项目,这一切都让我头晕目眩。 GET、PUT 和 POST 请求似乎正常工作,但我无法弄清楚为什么当我向后端发出成功的 POST 请求时,todos 列表没有自动更新。

我有一个 TodoList 组件,它循环通过 todosFiltered() 计算属性来显示待办事项。计算的属性返回到 Vuex 存储中的 getter todosFiltered。我还在这里使用created() 生命周期钩子在商店中调度一个操作,该操作发出初始GET 请求,然后在页面首次加载时在商店中填充一个名为todos 的数组。商店中的getter todosFiltered 返回state.todos,所以我假设当我的组件重新渲染时,它会从todosFiltered 中抓取状态中的新todos 数组,只是没有发生。我在这里想念什么?任何建议将不胜感激。

TodoList.vue

(我知道我必须为 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

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)
            })
        },
    }
})

编辑:当我添加待办事项时,Vue 开发工具中发生了以下情况——商店状态中的 todos 立即更新,TodoList 组件中的 todosFiltered 计算属性也反映了这一点——但是新的待办事项没有出现在列表中!奇怪。

【问题讨论】:

标签: javascript vue.js


【解决方案1】:

解决这个问题的一种方法是创建我喜欢称之为refresh() 的方法。

基本上,您的data() 方法中将有一个todos 的本地列表,refresh() 方法会将所有todos 从商店加载到本地todos 列表中,每次您执行操作,例如创建、删除或更新,您将调用 refresh 方法为您重新加载列表。

所以,在你的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>

实际上不要删除您已经拥有的内容并将其替换为 这个,只需将这里的内容应用到您的代码中。

【讨论】:

    【解决方案2】:

    问题是在 v-for 循环中使用了 $store.getter。 请尝试以下操作:

    1. 将你的计算设置为

      待办事项(){ 返回 this.$store.todos; }

    2. 将 v-for 循环更改为在 todos 中使用 todo

    3. 在循环中添加一个 v-if 条件,例如 v-if="filtered(todo)"
    4. 创建一个名为 filters 的新方法(或任何您喜欢的方法),并在其中添加您的“filteredTodos”逻辑,根据需要返回 true/false
    5. 如果您需要共享此代码,您可以随时使用 mixin 并在组件之间共享它

    希望这对你有用

    【讨论】:

    • 这没用 :( 上面的两个海报说计算属性不起作用,所以我需要找出其他东西。
    猜你喜欢
    • 2019-08-06
    • 2017-01-10
    • 1970-01-01
    • 2022-07-16
    • 2019-03-20
    • 2020-10-21
    • 1970-01-01
    • 2018-10-09
    • 2019-06-13
    相关资源
    最近更新 更多