Компонент Vue получает доступ к данным до того, как они станут доступны в хранилище Vuex

#javascript #vue.js #nuxt.js

#javascript #vue.js #nuxt.js

Вопрос:

У меня есть компонент с именем ProductArea , который отображает продукты, загруженные из Prismic API . Загружаемые продукты зависят от a category , который выбирается пользователем на боковой панели.

Я использую Vuex и изо всех сил пытаюсь создать поток, который позволяет избежать ситуации, когда category он еще недоступен в моем хранилище ( category также загружается из Prismic ).

Вот как ProductArea выглядит родительский элемент:

 <template>
  <div>
    <NavBar />
    <!-- <Header /> -->
    <main>
      <div v-if="!$fetchState.pending" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div class="flex-1 min-w-0 bg-white xl:flex">
          <Sidebar :navigation="navigation" />

          <ProductArea />
        </div>
      </div>
    </main>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'
import NavBar from '@/components/NavBar.vue'
import Sidebar from '@/components/Sidebar.vue'
import Header from '@/components/Header.vue'
import CategoryHeader from '@/components/CategoryHeader.vue'
import ProductGrid from '@/components/ProductGrid.vue'
import { mapActions } from 'vuex'
import { mapGetters } from 'vuex'

export default {
  name: 'App',
  components: {
    Sidebar,
    NavBar,
    Header,
    CategoryHeader
  },
  data() {
    return {
      navigation: null
    }
  },
  async fetch() {
    const component = this
    await this.fetchCategories()
    .then(function(navigationResult) {
      const navigation = component.$store.getters.navigation
      component.navigation = navigation
    })
  },
  fetchOnServer: true,
  methods: {
      ...mapActions({ fetchCategories: 'fetchCategories', fetchProducts: 'fetchProducts' })
  }
}
</script>
 

Я предполагал v-if="!$fetchState.pending" , что наличие ProductArea предотвратит создание до category тех пор, пока оно не будет загружено в хранилище, однако, похоже, это не так.

Вот ProductArea :

 <template>
    <div class="bg-white lg:min-w-0 lg:flex-1">
        <CategoryHeader :category="this.category" :products="this.products" />

        <div class="sm:p-6">
            <ProductGrid :category="this.category.primary.category" :products="this.products" />
        </div>
    </div>
</template>

<script lang="ts">
import { mapActions } from 'vuex'
import { mapGetters } from 'vuex'
import Locale from '@/types/locale'

export default {
    name: 'ProductArea',
    data() {
        return {
            category: this.$store.getters.category,
            products: Array
        }
    },
    async fetch() {
        const component = this

        await this.fetchProducts(this.category)
        .then(function(productsResult) {
            const products = component.$store.getters.products
            component.products = products
            console.log(products)
        })
    },
    fetchOnServer: true,
    methods: {
        ...mapActions({ fetchProducts: 'fetchProducts' })
    }
}
</script>
 

Вот ошибка, которую я получаю:

Ошибка в fetch(): ошибка типа: не удается прочитать свойство ‘products’ неопределенного

Эта ошибка относится к неопределенному category внутри fetchProducts вызываемого via fetch в ProductsArea компоненте.

Кто-нибудь может указать мне правильное направление? Каков был бы оптимальный поток здесь, чтобы предотвратить category доступ к нему до его доступности?

Ответ №1:

Вы можете установить категорию по умолчанию. Если вы не хотите этого делать, перенесите Vuex category в родительский файл и показывайте его только <ProductArea> тогда, когда он определен:

Родительский

 <ProductArea v-if="category" />
 
 computed: {
  ...mapGetters(['category'])
}
 

Это необходимо, потому что ваш v-if on $fetchState.pending проверяет только, загружены ли все категории, но для дочернего компонента вам также необходимо проверить, что a category был выбран.

Фактически, вы можете упростить весь свой код, сопоставив геттеры вместо хранения геттеров в переменных, что не является хорошей практикой. Эти переменные не будут обновляться реактивно при изменении средства получения. Вместо этого полностью удалите data параметры из обоих компонентов:

Родительский

 async fetch() {
  await this.fetchCategories();
}
 
 computed: {
  ...mapGetters(['category', 'navigation'])
}
 

Дочерний элемент

 async fetch() {
  await this.fetchProducts();
}
 
 computed: {
  ...mapGetters(['category', 'products'])
}
 

Другие улучшения:

  • Вы можете немного сократить mapActions вызовы:

    Родительский: ...mapActions(['fetchCategories'])

    Дочерний элемент: ...mapActions(['fetchProducts'])

Комментарии:

1. Отлично, спасибо. Есть пара других проблем с моим текущим кодом, которые означают, что я пока не могу это протестировать, но ясность в отношении лучших практик / что работает / что нет и т.д. — это то, что я искал. 👍