【问题标题】:How to set up parameters and arguments in Vuex mutations如何在 Vuex 突变中设置参数和参数
【发布时间】:2019-11-10 13:46:37
【问题描述】:

我正在使用 Vue.js、Vuex 和 Firebase 构建一个待办事项列表应用程序。该应用程序似乎可以正常工作,因为 Store 文件成功地管理了输入的 todo 项的检索和呈现(往返于 firestore)。但是,我仍然有一个关于在 Vuex 中设置参数的问题。突变中的 REMOVE_TODO 函数(参见 store.js)似乎需要两个参数,即使“id”是函数实际代码中引用的唯一参数。换句话说,如果我取出初始参数(在本例中为“state”),那么控制台将返回一个错误,内容为:“Function CollectionReference.doc() 要求其第一个参数的类型为非空字符串,但是它是:一个自定义对象对象”。我的问题是:如果“id”参数是函数中唯一实际使用的参数,为什么这个 REMOVE_TODO 函数需要两个参数才能正常运行?为什么需要另一个论点?下面是我的代码。谢谢!

app.vue

<template>
  <div id="app" class="container">
    <input class="form-control" :value="newTodo" @change="getTodo" placeholder="I need to...">
    <button class="btn btn-primary" @click="addTodo">Add New Post</button>
    <ul class="list-group">
        <li class="list-group-item" v-for="todo in this.$store.getters.getTodos" :key="todo.id">
            {{todo.title}}
            <div class="btn-group">
                <button type="button" @click="remove(todo.id)" class="btn btn-default btn-sm">
                <span class="glyphicon glyphicon-remove-circle"></span> Remove
                </button>
            </div>
        </li>
    </ul>
  </div>
</template>
<script>
export default {
  beforeCreate: function() {
    this.$store.dispatch('setTodo')
  },
  methods: {
    getTodo(event) {
      this.$store.dispatch('getTodo', event.target.value)
    },
    addTodo() {
      this.$store.dispatch('addTodo')
      this.$store.dispatch('clearTodo')
    },
    remove(id){
      this.$store.dispatch('removeTodo', id)
    }
  },
  computed: {
    newTodo() {
      return this.$store.getters.newTodo
    },
    todos(){
      return this.$store.getters.todos
    }
  }
}
</script>
<style>
body {
  font-family: Helvetica, sans-serif;
}
li {
  margin: 10px;
}
</style>

store.js

import Vue from 'vue'
import Vuex from 'vuex'
import db from '../firebase'

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    todos: [],
    newTodo: '',
    errors: ''
  },
  mutations: { //syncronous, committed
    GET_TODO: (state, todo) => {
      state.newTodo = todo
    },
    ADD_TODO: state => {
      db.collection('items').add({
        title: state.newTodo,
        created_at: Date.now(),
      }).then(function(){
        console.log('Document successfully added')
      })
      .catch((error) => {
        this.errors = error
      })
    },
    REMOVE_TODO: (state, id) => {
       if (id) {
         db.collection("items").doc(id).delete().then(function() {
           console.log('Document successfully deleted')
         })
         .catch((error) => {
           this.errors = error
         })
       } else {
         this.errors = 'Invalid ID'
       }
    },
    CLEAR_TODO: state => {
      state.newTodo = ''
    },
    SET_TODO: state => {
      let todos = []
      db.collection('items').orderBy('created_at').onSnapshot((snapshot) => {
        todos = []
        snapshot.forEach((doc) => {
          todos.push({ id: doc.id, title: doc.data().title })
        })
        state.todos = todos
      })
    }
  },
  actions: { //asyncronous, dispatched
    getTodo: (context, todo) => {
      context.commit('GET_TODO', todo)
    },
    addTodo: context => {
      context.commit('ADD_TODO')
    },
    removeTodo: (context, id) => {
      context.commit('REMOVE_TODO', id)
    },
    clearTodo: context => {
      context.commit('CLEAR_TODO')
    },
    setTodo: context => {
      context.commit('SET_TODO')
    }
  },
  getters: {
    newTodo: state => state.newTodo,
    getTodos: state => {
      return state.todos
    }
  }
})

【问题讨论】:

  • 嗨 JS_is_awesome18 突变是修改你必须给它的状态以更新你的状态可能是你可以在状态中有一个消息属性并做 state.message = "document deleted" 如果没问题or not state.message = "错误文档未删除".vuex.vuejs.org/guide/mutations.html
  • @Birante,感谢您的反馈,但我不确定这是否完全回答了问题。这是否意味着一个突变的函数,默认情况下,即使在函数的代码中没有使用状态,也需要状态作为参数?
  • 是的,你明白我认为这是一种最佳实践的一切。

标签: javascript firebase vue.js google-cloud-firestore vuex


【解决方案1】:

这与 Vuex 没有任何关系,它只是 JavaScript 函数调用的工作方式。参数是按位置而不是名称传递的。

您有 REMOVE_TODO: (state, id) =&gt; {,但名称 stateid 仅在函数内部真正重要。在函数之外,从调用者的角度来看,这些名称并不重要。它也可以很容易地成为REMOVE_TODO: (a, b) =&gt; {

当 Vuex 调用突变时,它会将 state 对象作为第一个参数传递,payload 作为第二个参数传递。它实际上是在调用:

mutations.REMOVE_TODO(state, payload)

我重申,参数的名称实际上并不重要,重要的是它们的位置。

这是 Vuex 内部的,不是你可以直接控制的。如果你自己调用这个函数,你可以传递任何你想要的东西,但你不是(也不应该是)。相反,你(非常正确地)打电话给commit。您将所需的 payload 传递给 commit,然后 Vuex 将完成剩下的工作,调用相关的突变并将 statepayload 传递给它。

将函数定义为REMOVE_TODO: (id) =&gt; { 不会更改调用者传递的参数。第一个参数仍然是state 对象。您已将其命名为 id,但这没有任何区别,它仍然是 state 对象。

一般来说,尝试删除第一个参数的问题是所有其他参数有效地向下移动,因此所需参数名称的位置不再匹配传递的参数的位置。从末尾删除一个参数没有这样的问题,因为其他参数不移动位置。因此,如果您只需要state 而不是payload,那么您可以删除第二个参数没问题。 API 设计者总是尝试将可选参数放在末尾,以便可以在不引起任何问题的情况下删除它们。

对于突变,payload 很容易不需要,但始终需要state。突变的全部意义在于改变state。如果您没有更改 state,那么您不应该使用突变。

如您的代码中所述,actions 是异步的,而 mutations 需要是同步的。但这不是你所拥有的。目前,您的突变中有很多对 Firebase 的异步调用。所有这些都需要转移到actions。请注意,Promises 始终是异步的,因此如果您发现自己在突变中调用 then,则说明您做错了。应该在 mutations 内的唯一部分是当您修改 state 时。

您还需要避免在 Vuex 商店中使用 this。你应该发现你需要的一切都可以从传递给函数的参数中访问。目前,您的突变中有this.errors = error 和类似的。我假设应该是state.errors = error

更新:

根据 cmets 的要求:

SET_TODO (state, todos) {
  state.todos = todos
}
setTodo ({commit}) {
  db.collection('items').orderBy('created_at').onSnapshot(snapshot => {
    const todos = []

    snapshot.forEach(doc => {
      todos.push({ id: doc.id, title: doc.data().title })
    })

    commit('SET_TODO', todos)
  })
}

请注意,这不会尝试处理可能的竞争条件。

【讨论】:

  • 感谢您的反馈。您能否提供一个将承诺转移到行动中重写商店的示例?我正在尝试重新配置我的商店以反映您所写的内容。然后在突变中被错误地写入。如果您可以提供一个 sn-p 代码来显示示例,那将非常有帮助。谢谢!
  • @JS_is_awesome18 完成。
猜你喜欢
  • 2023-04-03
  • 1970-01-01
  • 2018-05-07
  • 2017-10-05
  • 1970-01-01
  • 2019-03-15
  • 2018-06-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多