api композиции vue2 перед обновлением маршрута обновляет обновление после изменения маршрута

#vuejs2 #vue-composition-api

Вопрос:

У меня есть этот маршрут:

 {
  path: "/categories/:categorySlug",
  name: "product-list",
  meta: {
    title: "Categories",
  },
},
 

Когда компонент загружается впервые, он извлекает категорию и любые связанные с ней продукты. Проблема в том, что когда я меняю категорию slug, по какой-то причине категория обновляется нормально, но продукты этого не делают.
Я решил добавить beforeRouteUpdate, чтобы заставить продукты изменяться, и я настроил его следующим образом:

 export default defineComponent({
  name: "ProductList",
  components: { Brands, Chooser, Products },
  setup() {
    const instance = getCurrentInstance();
    const searchTerm = computed(() => {
      return instance.proxy.$route.params.searchTerm;
    });

    const {
      brands,
      brandError,
      brandFacets,
      brandsLoading,
      brandsHasMoreResults,
      brandsItemsToShow,
      brandsQuery,
      brandsTotal,
      brandsFetchMore,
    } = useSearchBrands(searchTerm, 12, [], true);
    const {
      category,
      categoryError,
      categoryLoading,
      products,
      productError,
      productFacets,
      productsLoading,
      productsHasMoreResults,
      productsItemsToShow,
      productsQuery,
      productsTotal,
      productsFetchMore,
    } = useListProducts(instance);

    return {
      brands,
      brandError,
      brandFacets,
      brandsLoading,
      brandsHasMoreResults,
      brandsItemsToShow,
      brandsQuery,
      brandsTotal,
      category,
      categoryError,
      categoryLoading,
      products,
      productError,
      productFacets,
      productsLoading,
      productsHasMoreResults,
      productsItemsToShow,
      productsQuery,
      productsTotal,
      brandsFetchMore,
      productsFetchMore,
    };
  },
  beforeRouteUpdate(to, from, next) {
    console.log(to);
    this.productsQuery.refetch();
    next();
  },
});
 

На первый взгляд казалось, что ничего не произошло, потому что, если бы я изменил маршрут, он все равно показал бы те же продукты, что и при первой загрузке. Но если я изменюсь снова, я заметил, что теперь на нем отображаются продукты с предыдущего изменения маршрута:

введите описание изображения здесь

И если я изменю маршрут от духовок к кофеваркам, он покажет духовки. Я не могу использовать beforeRouteEnter, потому что у меня нет доступа к этому, а использование beforeRouteLeave вообще не обновляет компонент, хотя в моих журналах я вижу, что запрос меняется.

Итак, подводя итог, когда я изменяю маршрут с помощью beforeRouteUpdate, я вижу в своих журналах, что запрос изменяется на правильную категорию, и я вижу возвращенные правильные продукты, но я не вижу результатов в компоненте, пока я снова не изменю маршрут (с тем же компонентом).

Кто-нибудь знает, как я могу это исправить?


Обновление Меня попросили отобразить код для моего списка продуктов. Я использую vue apollo, и у меня есть две общие функции, первая из которых-useGraphQuery, которая выглядит следующим образом:

 import { ref } from "vue-demi";

import { useQuery, useResult } from "@vue/apollo-composable";

export function useGraphQuery(params, gql, pathFn, clientId = "apiClient") {
  if (!params?.value)
    return {
      response: ref(undefined),
      loading: ref(false),
      error: ref(undefined),
      query: ref(undefined),
    };

  // TODO: figure our a way to skip the call if the parameters are null

  const { result, loading, error, query, fetchMore } = useQuery(gql, params, {
    clientId,
    //enabled: !!params?.value,
  });
  const response = useResult(result, null, pathFn);

  return { response, loading, error, query, fetchMore };
}
 

Все мои запросы graphql используют это.
Затем для поиска (т. е. поиска продукта) используется другая функция, называемая Usegraph-поиск:

 import { computed } from "@vue/composition-api";

import { useGraphQuery } from "./graph-query";

export function useGraphSearch(params, gql, pathFn) {
  const { response, loading, error, query, fetchMore } = useGraphQuery(
    params,
    gql,
    pathFn
  );

  const items = computed(() => {
    if (!response.value) return [];
    return response.value.items;
  });

  const facets = computed(() => {
    if (!response.value) return [];
    return response.value.facets;
  });

  const total = computed(() => {
    if (!response.value) return 0;
    return response.value.total;
  });

  const hasMoreResults = computed(() => {
    if (!response.value) return false;
    return response.value.hasMoreResults;
  });

  const itemsToShow = computed(() => params.value.search.itemsToShow);

  const more = () => {
    useGetMore(params.value, fetchMore);
  };

  return {
    error,
    facets,
    hasMoreResults,
    items,
    itemsToShow,
    loading,
    query,
    total,
    more,
  };
}

function useGetMore(params, fetchMore) {
  params.search.page  ;

  fetchMore({
    variables: params,
  });
}
 

On the route I mentioned before, there are 3 queries running. One of them seems to work without doing anything. that is the useGetCategory which looks like this:

 import { computed } from "@vue/composition-api";

import * as getCategoryBySlug from "@graphql/api/query.category.gql";

import { useGraphQuery } from "./graph-query";

export function useGetCategory(instance) {
  const params = computed(() => {
    const route = instance.proxy.$route;
    const slug = route.params.categorySlug;
    if (!slug) return;
    return { slug };
  });

  const { response, error, loading } = useGraphQuery(
    params,
    getCategoryBySlug,
    (data) => data.categoryBySlug
  );

  return { category: response, categoryError: error, categoryLoading: loading };
}
 

This updates regardless whether I called beforeRouteUpdate or not.
The second one is the useListProducts which looks like this:

 import { ComponentInternalInstance } from "@vue/composition-api";

import { useSearchCategoryProducts } from "@logic/search-products";
import { useTrackProductImpressions } from "@logic/track-product-impressions";
import { useTrackProductClick } from "@/_shared/logic/track-product-click";

export function useListProducts(instance: ComponentInternalInstance) {
  const {
    products,
    productError,
    productFacets,
    productsLoading,
    productsHasMoreResults,
    productsItemsToShow,
    productsQuery,
    productsTotal,
    productsFetchMore,
  } = useSearchCategoryProducts(instance);

  return {
    products,
    productError,
    productFacets,
    productsLoading,
    productsHasMoreResults,
    productsItemsToShow,
    productsQuery,
    productsTotal,
    productsFetchMore,
  };
}
 

as you can see here, this calls useSearchCategoryProducts which is just used to create the parameters and looks like this:

 export function useSearchCategoryProducts(
  instance: ComponentInternalInstance,
  orderBy = [{ key: "InVenue", value: "desc" }]
) {
  const params = computed(() => {
    const slug = instance.proxy.$route.params.categorySlug;
    if (!slug) return;
    const filters = createFilters("CategorySlug", [slug]);
    const request = createRequest(defaultParameters, 1, filters, orderBy);
    return { search: request };
  });

  return queryProducts(params);
}

function queryProducts(params) {
  console.log(params);
  const {
    error,
    facets,
    hasMoreResults,
    items,
    itemsToShow,
    loading,
    query,
    total,
    more,
  } = useGraphSearch(params, searchProducts, (data) => data.searchProducts);
  return {
    products: items,
    productError: error,
    productsLoading: loading,
    productFacets: facets,
    productsHasMoreResults: hasMoreResults,
    productsItemsToShow: itemsToShow,
    productsTotal: total,
    productsQuery: query,
    productsFetchMore: more,
  };
}
 

Вы можете увидеть console.log в частной функции queryProducts, в которой я вижу, что параметры обновляются.
Я знаю, что это многовато для понимания, но я создал usegraph-запрос и usegraph-поиск, чтобы гарантировать, что каждый запрос, который я создаю, одинаков и должен работать одинаково. Причина, по которой useGetCategory и useListProducts работают по-разному (т. Е. Категория меняется при изменении маршрута, а список продуктовнет), выше моего понимания, и именно по этой причине я вообще пытаюсь реализовать beforeRouteUpdate.

Код для настройки выглядит так, кстати:

 import {
  computed,
  defineComponent,
  getCurrentInstance,
} from "@vue/composition-api";

import Brands from "@components/brands/brands.component.vue";
import Chooser from "@components/chooser/chooser.component.vue";
import Products from "@components/products/products.component.vue";
import { useListProducts } from "./list-products";
import { useSearchBrands } from "@logic/search-brands";
import { useGetCategory } from "@logic/get-category";

export default defineComponent({
  name: "ProductList",
  components: { Brands, Chooser, Products },
  setup() {
    const instance = getCurrentInstance();
    const searchTerm = computed(() => {
      return instance.proxy.$route.params.categorySlug;
    });

    const { category, categoryError, categoryLoading } =
      useGetCategory(instance);

    const {
      brands,
      brandError,
      brandFacets,
      brandsLoading,
      brandsHasMoreResults,
      brandsItemsToShow,
      brandsQuery,
      brandsTotal,
      brandsFetchMore,
    } = useSearchBrands(searchTerm, 12, [], true);
    const {
      products,
      productError,
      productFacets,
      productsLoading,
      productsHasMoreResults,
      productsItemsToShow,
      productsQuery,
      productsTotal,
      productsFetchMore,
    } = useListProducts(instance);

    return {
      brands,
      brandError,
      brandFacets,
      brandsLoading,
      brandsHasMoreResults,
      brandsItemsToShow,
      brandsQuery,
      brandsTotal,
      category,
      categoryError,
      categoryLoading,
      products,
      productError,
      productFacets,
      productsLoading,
      productsHasMoreResults,
      productsItemsToShow,
      productsQuery,
      productsTotal,
      brandsFetchMore,
      productsFetchMore,
    };
  },
  beforeRouteUpdate(to, from, next) {
    console.log(to);
    this.brandsQuery.refetch();
    this.productsQuery.refetch();
    next();
  },
});
 

Ответ №1:

Вы to ни для чего не используете обработчик обновления маршрута. Если ваш код загрузки зависит от маршрута, он будет содержать предыдущую информацию, так как событие происходит до его обновления.

Вам нужно будет предоставить информацию о to маршруте коду, который выполняет загрузку, или использовать событие после.

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

1. Я не совсем понимаю, что вы имеете в виду? маршрут «кому» уже содержит параметры, так как он изменяет маршрут, и метод повторной выборки использует этот параметр для получения данных. И что вы имеете в виду, говоря о последующем событии? отличается ли это от того, что было до выхода ?

2. @r3plica Как он его использует? Вы вызываете эту функцию с нулевыми аргументами. Куда он денется to ? Включите весь необходимый код

3. в коде настройки я создаю вычисляемое свойство params, которое принимает значение categorySlug. Метод повторной выборки использует это вычисленное свойство, и я вижу, что значения верны

4. @r3plica Так не могли бы вы тогда включить этот код? Невозможно сказать, что не так с кодом, который мы не видим, и значениями, которые мы не можем проверить

5. Да, но я должен предупредить вас, что это vue apollo, и любой вопрос, который я задавал до сих пор, кажется, ставит людей в тупик 🙁

Ответ №2:

Так что мне удалось это исправить. Все зависело от моей политики кэширования. Я как раз этим и занимался:

 const typePolicy = {
  keyArgs: ["search", ["page", "skip"]],
  // Concatenate the incoming list items with
  // the existing list items.
  merge(existing: any = {}, incoming: any) {
    const items = (existing.items ? existing.items : []).concat(incoming.items);
    const item = { ...existing, ...incoming };
    item.items = items;
    return item;
  },
};

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        searchCategories: typePolicy,
        searchBrands: typePolicy,
        searchPages: typePolicy,
        searchProducts: typePolicy,
      },
    },
  },
});
 

Так что мои результаты по страницам всегда добавлялись в мой текущий список, но по какой-то причине это вызывало проблему. Когда я переключился на это:

 const cache = new InMemoryCache();
 

Мои проблемы были решены.