Search code examples
vue.jsnuxt.jsvuex

Nuxt component not updating but store is updating as expected


After some good help already here (i'm new with Vue and I'm learning a lot which is cool) I have another question. The state of my component is updating as expected. When inputting a value in the search field this value will be send to the API which will respond and update the state. The problem now is that I will need to click routes (and not use refresh/f5) before I can actually see the updated components in my current view. My code so far:

search.vue (don't mind the student interpolation, dummy data)

<div class="mx-auto mt-2 max-w-7xl sm:px-6 lg:px-8">
      <div class="flex justify-between flex-1 max-w-xl px-4">
        <div class="px-4 py-6 sm:px-0">
          <ul id="tutors" class="grid grid-cols-1 gap-6">
            <li
              v-for="tutor in tutors"
              :key="tutor.name"
              class="overflow-hidden bg-white border rounded-lg shadow-md"
            >
              <div class="flex">
                <div class="w-2/3">
                  <img
                    class="flex-shrink-0 object-cover w-full h-64 mx-auto bg-black"
                    :src="student.imageUrl"
                    :alt="student.imageAlt"
                  />
                </div>
                <div class="p-6">
                  <div
                    class="text-xs font-semibold leading-snug tracking-wide text-gray-500 uppercase"
                  >
                    {{ student.subject }} &bull; {{ student.age }} jaar
                  </div>
                  <h4 class="text-lg font-semibold leading-5 tracking-wide">
                    {{ tutor.name }}
                  </h4>
                  <div class="mt-2">
                    {{ student.hourlyRate }}€

                    <span class="text-sm text-gray-600">per uur</span>
                  </div>
                  <div class="mt-2">
                    <span class="font-semibold text-light-blue-800"
                      >{{ student.rating }}/5 sterren</span
                    >
                    <span class="text-sm text-gray-600 truncate">
                      (na {{ student.reviewCount }} reviews)
                    </span>
                  </div>
                  </div>
                </div>
              </div>
            </li>
          </ul>

search.vue JS

<script>
import { mapGetters, mapState } from 'vuex'

export default {
  name: 'Zoeken',
  components: {},

  data: () => ({
    postcode: '',
    attributes: [],
  }),
  computed: {
    ...mapGetters(['isAuthenticated', 'loggedInUser']),
    ...mapState(['tutors']),
  },
  methods: {
    async fetchTutors() {
      const postcode = this.postcode
      await this.$store.dispatch('loadAllTutors', postcode)
    },
  },
  layout: 'app',
  middleware: 'auth',
}
</script>

store/index.js

export const getters = {
  isAuthenticated(state) {
    return state.auth.loggedIn
  },

  loggedInUser(state) {
    return state.auth.user
  },
}

export const state = () => ({})

export const mutations = {
  SET_TUTORS(state, val) {
    if (val == null || val.update === undefined) {
      state.tutors = val
    } else {
      const update = val.update
      state.tutors = { ...state.tutors, ...update }
    }
  },
}

export const actions = {
  loadAllTutors({ commit }, postcode) {
    this.$axios
      .post('http://notawanker.com/tutors/search', {
        postcode,
      })

      .then(({ data }) => {
        commit(
          'SET_TUTORS',
          data.map((item) => item.attributes)
        )
      })
      .catch((error) => console.log(error))
  },
}

This will be the main component of the app where it will be possible to search for nearby users. The main part and the API are working but making it reactive is still a problem. I tried a few different ways without success. Many thanks in advance.


Solution

  • As i understand in your code you're using SET_TUTORS to populate the tutors state in first instance, but you use it aswell to update after a search ?

      SET_TUTORS(state, val) {
        if (val == null || val.update === undefined) {
          state.tutors = val
        } else {
          const update = val.update
          state.tutors = { ...state.tutors, ...update }
        }
      },
    }
    

    If so, you can't use the update you're doing is not reactive, it will be updated in store, but vue reactivity won't trigger.

    you need to use Vue.set

     } else {
          const update = val.update
          this.$set(state.tutors, myProperty, myNewValue }
        }
    

    For updating more than a property:

     } else {
          const update = val.update
    
    // for replacing the whole object
          this.state.tutors = Object.assign({}, this.state.tutors, update)
    
    // for replacing the a nested object in tutors
          this.state.tutors = Object.assign({}, this.state.tutors.myObjectToUpdate, update)
        }
    

    Let me know if it has worked, in the meantime i could advice you to read more about reactivity in vuejs, it will save you some time once in a while :)

    https://v2.vuejs.org/v2/guide/reactivity.html#For-Objects

    Following your comment :

    When you're updating a single property from the state, you can go with this.$set(), when you want to update more properties at once, you need to use object.assign

    Example:

    tutors: {
     types: {
      women: 32
     } 
    }
    And you just want to add the men property number, you will do => this.$set(state.tutors.types, men, 32)
    

    The above response handle reactivity, and myProperty is the name of the property object you want to add

    Based on your question, since SET_TUTORS is multiple usages, i would write the code this way to update when it needs to be updated, and if it's the base SET, that should override everything already in the state:

     SET_TUTORS(state, val) {
        if (val == null || val.update === undefined) {
          state.tutors = Object.assign({}, val)
        } else {
          // will create a new object based on your current state and the updated values
          state.tutors = Object.assign({}, this.state.tutors, update)
        }
      },
    

    Hope it helps