Как настроить параметры и аргументы в мутациях Vuex - PullRequest
0 голосов
/ 28 июня 2019

Я создаю приложение со списком задач, используя Vue.js, Vuex и Firebase. Похоже, что приложение работает должным образом, так как файл Store успешно управляет поиском и рендерингом введенных элементов todo (в и из firestore). Тем не менее, у меня все еще есть вопрос о настройке параметров в Vuex. Функция REMOVE_TODO в мутациях (см. Store.js), по-видимому, требует двух аргументов, хотя «id» является единственным аргументом, на который ссылается фактический код функции. Другими словами, если я уберу начальный аргумент (в данном случае «состояние»), консоль вернет ошибку, которая гласит: «Функция CollectionReference.doc () требует, чтобы ее первый аргумент имел тип непустую строку, но это было: пользовательский объект ". Мой вопрос: почему эта функция REMOVE_TODO должна иметь два аргумента для правильной работы, если аргумент "id" является единственным, фактически используемым в функции? Почему нужен другой аргумент? Вот мой код ниже. Спасибо!

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

1 Ответ

1 голос
/ 29 июня 2019

Это на самом деле не имеет ничего общего с Vuex, просто работает вызов функций JavaScript. Аргументы передаются по позиции, а не по имени.

У вас есть REMOVE_TODO: (state, id) => {, но имена state и id действительно важны только внутри функции. За пределами функции, с точки зрения вызывающего, эти имена не имеют значения. С таким же успехом это может быть REMOVE_TODO: (a, b) => {.

Когда Vuex вызывает мутацию, он передает объект state в качестве первого аргумента и payload в качестве второго аргумента. Фактически это звонит:

mutations.REMOVE_TODO(state, payload)

Я повторяю, что имена аргументов на самом деле не имеют значения, только их позиции.

Это внутреннее внутри Vuex, а не то, над чем у вас есть прямой контроль. Если бы вы вызывали функцию самостоятельно, вы могли бы передавать все, что хотите, но это не так (и не должны). Вместо этого вы (совершенно правильно) звоните commit. Вы передаете желаемый payload на commit, а Vuex сделает все остальное, вызвав соответствующую мутацию и передав ей state и payload.

Определение функции как REMOVE_TODO: (id) => { вместо этого не изменит аргументы, переданные вызывающей стороной. Первым аргументом все равно будет объект state. Вы назвали его id, но это не имеет значения, это все равно будет объект state.

В общем, проблема с попыткой отбросить первый аргумент заключается в том, что все остальные аргументы эффективно перемешиваются, поэтому позиции имен желаемых аргументов больше не соответствуют позициям передаваемых аргументов. Нет такой проблемы с отбрасыванием аргумента с конца, так как другие аргументы не перемещают позиции. Так что если вам нужен state, но не payload, тогда вы можете просто оставить второй аргумент без проблем. Разработчики API всегда стараются довести до конца необязательные аргументы, чтобы их можно было отбрасывать, не вызывая проблем.

Для мутации payload очень легко может быть ненужным, но state всегда необходим. Весь смысл мутации в том, чтобы изменить state. Если вы не меняете state, вам не следует использовать мутацию.

Как отмечено в вашем коде, actions являются асинхронными, тогда как mutations должны быть синхронными. Но это не то, что у вас есть. В настоящее время у вас много асинхронных вызовов Firebase внутри ваших мутаций. Все это нужно перенести в actions. Обратите внимание, что Обещания всегда асинхронны, поэтому, если вы обнаружите, что вызываете then внутри мутации, вы делаете это неправильно. Единственная часть этого, которая должна быть внутри mutations, это когда вы модифицируете state.

Вам также следует избегать использования this в магазине Vuex. Вы должны обнаружить, что все, что вам нужно, доступно из аргументов, переданных функции. В настоящее время у вас есть this.errors = error и подобные мутации. Я предполагаю, что это должно быть state.errors = error.

Обновление:

В соответствии с просьбой в комментариях:

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

Обратите внимание, что здесь не предпринимается попыток справиться с возможными условиями гонки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...