Blake Geist
Blake Geist

Reputation: 285

Nuxt / Vuex / Vue Reactivity Issue Increment

Hi everyone I am I having some difficulty when working with Nuxt and Vuex.

I am trying to run through the example Vuex / Nuxt Classic Mode. https://nuxtjs.org/guide/vuex-store/

After clicking my increment button I dont see the number go up. My page just stays at 0, I can see within the console that the state knows the number is no longer 0 but not on the screen, as if it doesnt know to be reactive.

My assumption is that I have miss configured something somewhere and my 0 is not the actual state, but I created some copy of it somehow.

Here is my button within my template.

<button @click="inc">{{ counter }}</button>

Here is my inc function within my methods.

inc () {
  this.$store.commit('increment')
},

Here is my computed

computed: {
  counter () {
    return this.$store.getters.counter
  }
}

Here is my Vuex/index.js file contained within the store folder.

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const createStore = () => {
  return new Vuex.Store({
    state: () => ({
      counter: 0
    }),
    getters: {
       counter: state => state.counter
    },
    mutations: {
      increment (state) {
        state.counter++
      }
    }
  })
}

export default createStore

Commercial Photography

Update: Included more code snippets, and replied to existing comments.

@Boussadjra Brahim @xaviert, thank you both for weighing in, I appreciate the assistance.

@Boussadjra Brahim - Yes, I had tried using an action that called the mutation, that didnt seem to get me there either. I also tried adjusting the state via the action alone, and wasnt able to make any changes, however that seems correct, as I am under the impression that actions call mutations to make state changes and do not themselves do so, please correct me if you know more. I am 100% open to the idea that I did not attempt it correctly. Below is that action that didnt do anything and the one that called the mutation

actions: {
  increment (state) {
    state.counter++
  }
},   

And here is the version with the action calling the mutation.

actions: {
  incrementCounterUp () {
    this.commit('increment')
  }
},

@xaviert - I have tried starting the server over, and have also tried to see if an nuxt build followed by a firebase serve, to see if maybe that helped. It did not. My normal server start is 'npm run dev'. In hopes that you/anyone else may be able to find my mistake below is my full _id.vue component and also my nuxt.config.js file as maybe that's it. Its still pretty raw and could use a lot of refactoring so hope you can sort through it well enough.

_.id.vue

<template>
  <div class="product">
    <div class="product-image">
      <div class="product-image-img">
        <img v-bind:src="product.image_file" width="450px;"/>
      </div>
    </div>
    <div class="product-details-wrapper">
      <div class="product-details">
        <h1>
          <div v-if="this.$route.query.editPage">
            <textarea @input="updateTextArea" ref="textarea2" v-model="product.item_name" type="text" />
          </div>
          <div v-else>{{product.item_name}}</div>
        </h1>
        <div class="product-description">
          <div class="product-description-text" v-if="this.$route.query.editPage">
            <textarea @input="updateTextArea" ref="textarea" v-model="product.description" type="text" />
          </div>
          <div class="product-description-text" v-else v-html="product.description"></div>
        </div>
        <p class="product-brand"><strong>Brand - </strong> {{product.brand_name}}</p>
        <hr />
        <div class="product-price">
          <div v-if="this.$route.query.editPage">
            <strong>Original Price - </strong>
            <input v-model="product.msrp" type="text" />
          </div>
          <div v-else class="product-msrp">
            <strong>Original Price - </strong>
            <span class="strike">${{product.msrp}}</span>
          </div>
          <div v-if="this.$route.query.editPage">
            <strong>Sale Price - </strong>
            <input v-model="product.price" type="text" />
          </div>
          <div v-else class="product-sale-price">
            <strong>Sale Price - </strong>
            <span class="">${{product.price}}</span>
          </div>
          <div class="product-price">
            Quantity x
            <input @input="updateQuantity" v-model="quantity" min="1" class="" type="number" value="1" />
          </div>
          <button @click="inc">{{ counter }}</button>
        </div>
      </div>
    </div>
    <div v-if="this.$route.query.editPage" class="update-product">       <button @click="updateProduct(product)">Update</button></div>
    <div class="footer">
      <router-link to="/privacy-policy" target="_blank">Privacy</router-link> |
      <router-link to="/terms" target="_blank">Terms</router-link>
    </div>

  </div>
</template>

<script>
// @ is an alias to /src

import firebase from '@/services/fireinit'
import foo from '@/components/foo'
const db = firebase.firestore()
export default {
  name: 'ProductPage',
  head () {
    return {
      title: this.product.item_name
    }
  },
  components: {
    foo
  },
  data: function () {
    return {
      product: {},
      image: '',
      name: 'Checkout',
      description: '',
      currency: 'USD',
      amount: '',
      msrp: '',
      quantity: 1
    }
  },
  methods: {
    inc () {
      this.$store.dispatch('incrementCounterUp', true)
    },
    updateProduct: function (product) {
      db.collection('products').doc(product.item_id).set(product)
        .then(function () {
          console.log('Document successfully written!')
        })
        .catch(function (error) {
          console.error('Error writing document: ', error)
        })
    },
    updateQuantity () {
      this.product.msrp = (this.quantity * this.product.orgMsrp)
      this.product.msrp = Math.round(100 * this.product.msrp) / 100
      this.product.price = this.quantity * this.product.orgPrice
      this.product.price = Math.round(100 * this.product.price) / 100
    },
    updateTextArea () {
      this.$refs.textarea.style.minHeight = this.$refs.textarea.scrollHeight + 'px'
      this.$refs.textarea2.style.minHeight = this.$refs.textarea2.scrollHeight + 'px'
    }
  },
  async asyncData({app, params, error}) {
    const ref = db.collection("products").doc(params.id)
    let snap
    let thisProduct = {}
    try {
      snap = await ref.get()
      thisProduct = snap.data()
      thisProduct.orgMsrp =  thisProduct.msrp
      thisProduct.orgPrice =  thisProduct.price
    } catch (e) {
      // TODO: error handling
      console.error(e)
    }
    return {
      product: thisProduct
    }
  },
  mounted () {
    if(this.$refs.textarea) {
      this.$refs.textarea.style.minHeight = this.$refs.textarea.scrollHeight + 'px'
      this.$refs.textarea2.style.minHeight = this.$refs.textarea2.scrollHeight + 'px'
    }
  },
  computed: {
    counter () {
      return this.$store.getters.counter
    }
  }
}
</script>


<style lang="less">
body {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
  margin: 0
}
p{
  margin-top: 1em;
  margin-bottom: 1em;
}
html, body, #__nuxt, #__layout, .default,  .product{
  height: 100%;
}
.product {
  justify-content: center;
  display: flex;
  max-width: 1150px;
  margin: 0 auto;
  flex-wrap: wrap;
  align-items: center;
  padding: 0 24px;
  &-price{
    input {
      border: 1px solid;
      padding: 0 .3em;
      text-align: center;
      width: 50px;
    }
  }
}
  .product-details{
    width: 100%;
    textarea {
      width: 100%;
      font-size: inherit;
      color: inherit;
      font-family: inherit;
      font-weight: inherit;
      height: initial;
      resize: none;
      background-color: transparent;
      border: none;
    }
    h1{
      font-size: 1.9rem;
      margin: 15px 0 20px;
    }
    hr{
      width: 50%;
      margin: .5rem 0px;
    }
    p{

    }
  }
  .product-description-text{
    margin: 10px 0;
  }
  .product-image, .product-details-wrapper{
    align-items: center;
    display: flex;
    justify-content: center;
  }
  .product-details-wrapper{
    flex: 0 1 535px;
  }
  .product-image{
    flex: 0 1 535px;
    img{
      width: 100%;
    }
  }
  .product-price{
    .strike{
      text-decoration: line-through;
    }
    button{
      display: flex;
      width: 150px;
      height: 50px;
      border-radius: 5px;
      justify-content: center;
      font-size: 24px;
      margin-top: 20px;
      &:hover{
        cursor: pointer;
        background-color: #f1f1f1;
        box-shadow: 3px 3px 11px -1px rgba(0, 0, 0, 0.48);
      }
    }
  }
  .product-sale-price{
    color: #f30000;
  }
  .footer {
    flex: 1 1 100%;
    text-align: center;
    color: #ccc;
    margin-top: 25px;
    padding: 15px;
    a {
      color: #ccc;
      text-decoration: none;
      &:hover{
        text-decoration: underline;
      }
    }
  }
  .update-product{
    position: absolute;
    top: 0;
    text-align: center;
  }

</style>

nuxt.confgs.js

const pkg = require('./package')
const { STRIPE_TOKEN } = process.env;

module.exports = {
  vue: {
    config: {
      productionTip: false,
      devtools: true
    }
  },
  buildDir: './functions/nuxt',
  mode: 'universal',

  /*
  ** Headers of the page
  */
  head: {
    title: pkg.name,
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: pkg.description }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },

  /*
  ** Customize the progress-bar color
  */
  loading: { color: '#fff' },

  /*
  ** Global CSS
  */
  css: [
  ],

  /*
  ** Plugins to load before mounting the App
  */

  /*
  ** Nuxt.js modules
  */
  modules: [
    // Doc: https://github.com/nuxt-community/axios-module#usage
    '@nuxtjs/axios',
    'nuxt-stripe-module'
  ],
  stripe: {
    version: 'v3',
    publishableKey: 'pk_test_XXX',
  },
  /*
  ** Axios module configuration
  */
  axios: {
    // See https://github.com/nuxt-community/axios-module#options
  },

  /*
  ** Build configuration
  */
  build: {
    /*
    ** You can extend webpack config here
    */
    publicPath: '/public/',
    vendor: [],
    extractCSS: true,
    bable: {
      presets: [
        'es2015',
        'stage-8'
      ],
      plugins: [
        ['transform-runtime', {
          'polyfill': true,
          'regenerator': true
        }]
      ]
    },
    extend (config, { isDev }) {
      if (isDev && process.client) {
        config.module.rules.push({
          enforce: 'pre',
          test: /\.(js|vue)$/,
          loader: 'eslint-loader',
          exclude: /(node_modules)/
        })
      }
    },
    router: {
      middleware: 'router-auth'
    }
  },
  plugins: [
    { src: '~/plugins/fireauth', ssr: true }
  ]
}

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const createStore = () => {
  return new Vuex.Store({
    state: () => ({
      counter: 0
    }),
    actions: {
      incrementCounterUp () {
        this.commit('increment')
      }
    },
    getters: {
      counter: state => state.counter
    },
    mutations: {
      increment (state) {
        state.counter++
      }
    }
  })
}

export default createStore

Upvotes: 3

Views: 2278

Answers (1)

Blake Geist
Blake Geist

Reputation: 285

At the end of the day, I was not able to identify exactly what in my application was the error.

I assume that my during my initialization, configuration or development I touched something that should not have been touched, installed something that should not have be installed, messed with a package I should not have or was to bold in my nuxt.config.js changes.

I created a new nuxt app following the same install instructions. https://nuxtjs.org/guide/installation/

Moved the above _id.vue component exactly as it is and once I got dependencies updated it worked perfectly as seen in the image below.

Thank you very much @Boussadjra Brahim, @xaviert, @Andrew1325 for you assistance.

enter image description here

Upvotes: 3

Related Questions