ARNON
ARNON

Reputation: 1217

How to push to an array via a Mutation in Vuex?

At the time I was writing this question I found the solution to my problem, but even so I decided to share it with the community to see if I'm solving the problem in the best way possible.

Given a summary of my Store:

//  store/index.js

const store = createStore({
  state: {
    userBooks: [],
  }
  mutations: {
    setUserBooks(state, val) {
      state.userBooks.push(val);
    },
  actions: {
    addBook({ commit }, payload) {
      commit("setUserBooks", payload);
  }
})

I'm calling the action like this:

//  Add.vue

methods: {
  addBook(book) {
    this.$store.dispatch("addBook", book);
  },
}

This was giving me the following error:

Uncaught (in promise) TypeError: state.userBooks.push is not a function

I had already checked this solution: Push to vuex store array not working in VueJS. And that's exactly what I tried, but I got the above error.

How I solved the problem:

I noticed that the book object was coming into the function as a proxy object. With that in mind, I turned the proxy object into a regular object as follows:

addBook(book) {
  book = Object.assign({}, book);
  this.$store.dispatch("addBook", book);
}

Why does the problem happen?

I confess that I still don't understand why the problem occurs. book is obtained via the v-for of books. books is assembled from a Google Books API query. The query is done using axios.get().then()

The console.log(this.books) already returns me a proxy object and I confess that I don't know if this is the expected behavior and if I should try to change it.

Anyway the problem is solved, but if anyone has any different approach I would be very happy to learn something new.


EDIT: More code

I decided to edit the question to show how books are generated and populated.

<template>
    <figure v-for="(book, index) in books" :key="index">
      <Button text="+" @click="addBook(book)" />
      <!-- I omitted the use of the other elements to make things more objective. -->
    </figure>
</template>

<script>
export default {
  data() {
    return {
      books: {},
    };
  },
  methods: {
    search() {
      axios
        .get(`https://www.googleapis.com/books/v1/volumes?q=${this.seek}`)
        .then((response) => {
          this.books = response.data.items.map((item) => ({
            id: item.id,
            title: item.volumeInfo.title,
            authors: item.volumeInfo.authors || [this.$t("book.unknown-author")],
            ISBN: item.volumeInfo.industryIdentifiers?.[0].identifier ?? item.id,
            thumbnail: item.volumeInfo.imageLinks?.thumbnail ?? this.noCover,
          }));
        })
        .catch((error) => console.error(error))
    },
    addBook(book) {
      // Object.assign({}, book)
      book = { ...book };
      this.$store.dispatch("addBook", book);
    },
  },
};
</script>

Upvotes: 2

Views: 1718

Answers (1)

Mises
Mises

Reputation: 4623

Another new faster way is spread operator. You create a new object and spred variables inside book object. It works same as book = Object.assign({}, book)

book = { ...book }

Bellow more examples of usage of spred operator:

  • You can use it in arrays to if val is an array with and object if not just don't type ... before val.
setUserBooks(state, val) {
      state.userBooks = [...state.userBooks, ...val];
}
  • Or for example you have big object called user and you have in this object his address object and he wants to change it.
setUser(state, address) {
      state.user = {...state.user, address};
}

Upvotes: 1

Related Questions