React Query Key Factory pattern ishlatish tajribasi

React Query Key Factory pattern ishlatish tajribasi

1. Kirish: so'rov kaliti nega muhim?

React Query (TanStack Query) ni birinchi bor joriy etayotganda aksariyat shunday boshlaydi.

useQuery({ queryKey: ['product-list'], queryFn: fetchProducts });
useQuery({ queryKey: ['product', productId], queryFn: () => fetchProduct(productId) });
useQuery({ queryKey: ['project-health', productId], queryFn: () => fetchHealth(productId) });

Komponentlar soni kam bo'lganda muammo yo'q. Ammo domenlar o'sib, jamoa a'zolari ko'payib, funksiyalar to'planib qolsa, bir payt keladi, bunday vaziyat yuzaga keladi.

  • "mahsulotga oid keshni butunlay o'chirishim kerak, kalitlar qayerda?"

  • "['product', id] deb yozdim, lekin kimdir ['products', id] qilib yozdi va ikkalasi boshqacha keshni ko'rayotgan edi"

  • "bu so'rov kalitiga parametr qo'shganda, mavjud kesh shunchaki qoldi va stale ma'lumotlar ko'rsatildi"

React Query'da so'rov kaliti oddiy identifikator emas. Keshning manzili va, bekor qilishning birlikdir. Bu yerda "bekor qilish" React Query'ga "bu ma'lumot endi eski, shuning uchun keyingi zaruratda serverdan qaytadan olib kel" deb aytish demakdir.

queryClient.invalidateQueries({ queryKey: ['product'] }) chaqirilganda, kalitlar massivining old qismi ['product'] bilan boshlanadigan barcha so'rovlar bir marta bekor qilinadi. Masalan, ['product', 'list'] ham, ['product', '123'] ham barcha holatlarni o'z ichiga oladi. Buni "prefix matching (old qism mos kelishi)" deb ataydigan bo'lsak, oddiy qilib aytganda, papka tuzilmadek yuqori kalit pastki kalitlarni to'liq o'z ichiga oladigan usuldir.

Bu tuzilmani har bir faylda alohida belgilab qo'ysak, keyinchalik hech kim umumiy tuzilmani tushunmay qoladi. Ushbu maqolada bizning jamoamiz ushbu muammoni hal qilish uchun qo'llagan Query Key Factory naqshni tanishtiramiz.

2. Antipattern dan Query Key Factory ga

Antipattern: kalitlarni tarqatganda yuzaga keladigan narsalar

Eng keng tarqalgan muammo holati. ['products'] va ['product'] boshqa kalitlar, va ['project-health'] mahsulotga umuman bog'liq ko'rinmaydi, lekin aslida "mahsulotning ma'lumotlari o'zgarishi bilan birga bekor qilinishi kerak bo'lgan" narsalar.

  • Bekor qilish kodini har safar yozganimda, tegishli kalitlarni to'g'ridan-to'g'ri eslab qolib sanab o'tishim kerak.

  • Kalitdagi xatolar yuz berganda qusurlarni ko'rsatuvchi xatolar bo'lmaganligi sababli de-bug qilish qiyin.

  • Parametrlar o'zgartirilganda yoki kalit tuzilmalari o'zgartirilganda barcha foydalanish joylarini topish va tuzatish zarur.

Hal qilish: domen bo'yicha Key Factory

Hal qilish usuli — so'rov kalitlarini domen bo'yicha ob'ekt (fabrika) sifatida markaziy ravishda boshqarishdir.

// product.cm.keys.ts
export const productCmKeys = {
  all: () => ['product', 'cm'] as const,
  lookups: (params: { stageId?: string }) =>
    [...productCmKeys.all(), 'findProductLookups', params] as const,
  projectHealthSummaries: (params: { productId?: string }) =>
    [...productCmKeys.all(), 'findProjectHealthSummaries', params] as const,
  releaseTimeline: (params: { productId?: string }) =>
    [...productCmKeys.all(), 'findReleaseTimeline', params] as const,
  productNode: (params: { productId?: string }) =>
    [...productCmKeys.all(), 'findProductNode', params] as const,
};

Shablonning asosiy qoidalari ikki xil. Birinchidan, all() umumiy ildizni (root) qaytaradi. Barcha pastki kalitlar all() ni oldinga tarqatib (...spread) qo'shilganligi sababli, invalidateQueries({ queryKey: productCmKeys.all() }) yordamida bu domenni bir marta yo'q qilish mumkin. Ikkinchidan, har bir funksiya parametrlarni (shart qiymatlari) qabul qilib, tugallangan kalitni qaytaradi. React Query kalitni ichki ravishda ishonchli xesh qiymatiga aylantirib solishtirishi sababli, { productId: '123' } va { productId: '456' } avtomatik ravishda bir-biridan alohida keshda saqlanadi.

3. queryOptions / useQueries / invalidateQueries bilan birga

queryOptions: so'rov sozlamalarini bitta joyda jamlash

Query Key Factory queryOptions yordamchi (TanStack Query v5 da qo'shilgan util funksiyalar) bilan birga ishlatganda yanada kuchli bo'ladi. So'rov kalitlari va fetching funksiyasini (queryFn) bitta funksiya sifatida bog'laganimizda, hook orqali ishlatganimizda va boshqa vaziyatlarda ham bir xil sozlamalarni qayta ishlatishimiz mumkin.

// findProjectHealthSummaries.query.ts
export const findProjectHealthSummariesQuery = (params: Params) =>
  queryOptions<FetchResponse<ProjectHealthRdo[]>>({
    queryKey: productCmKeys.projectHealthSummaries(params),
    queryFn: () => ProductProjSeekApi.findProjectHealthSummaries(params).then((res) => res.data),
});
// useFindProjectHealthSummaries.ts
export const useFindProjectHealthSummaries = (params: Params = {}) => {
  const { data, isLoading } = useQuery(findProjectHealthSummariesQuery(params));
  return { projectHealthSummaries: data?.fetchResult ?? ([] as ProjectHealthRdo[]), isLoading };
};
// 사용 예시
await queryClient.prefetchQuery(findProjectHealthSummariesQuery({ productId }));

React'da hooklarni komponent tashqarisida yoki oddiy funktsiya ichida chaqirish mumkin emas. Ammo queryOptions orqali sozlamalarni ajratib qo'ysak, server tomondan render qilish yoki ekran kirishidan oldin ma'lumotlarni oldindan yuklab olish (prefetch) vaziyatlarida ham bir xil sozlamalarni to'liq ishlatishimiz mumkin. queryOptions qaytaradigan ob'ekt useQuery va prefetchQuery ga avtomatik ravishda beriladi.

useQueries: bir vaqtning o'zida bir nechta so'rov yuborish

useQuery bir vaqtning o'zida bitta ma'lumot olib keladi. Ammo bir nechta loyihaning batafsil ma'lumotlarini bir vaqtning o'zida olib kelish zarur bo'lsa, useQueries dan foydalanamiz. findAgileProjectQuery sozlamalarni qaytaruvchi funksiya bo'lgani uchun, projectIds massivini map orqali aylanib, so'rovlar ro'yxatini toza holda yaratishimiz mumkin. Har bir loyiha o'ziga xos kalitga ega bo'lgani uchun kesh ham mustaqil ravishda boshqariladi, biri yo'q bo'lsa, qolganlariga ta'sir etmaydi.

export const useFindAgileProjectDetails = (projectIds: string[]) => {
  const results = useQueries({
    queries: projectIds.map((id) => findAgileProjectQuery({ projectId: id })),
  });
  const agileProjectDetails: AgileProjectDetailRdo[] = results
    .map((r) => r.data?.fetchResult)
    .filter((d): d is AgileProjectDetailRdo => d != null);
  const isLoading = results.some((r) => r.isLoading);
  return { agileProjectDetails, isLoading };
};

invalidateQueries: doirani tanlash va keshni yo'q qilish

Ushbu shablonning eng amaliy qismi. Yo'q qilish doirasini "butun domen" → "ma'lum so'rov" → "ma'lum parametrli so'rov" ga bosqichma-bosqich toraytirib tanlash mumkin. Qanday kalitlar borligini eslab qolish shart emas, kodda productCmKeys. ni kiritib qo'ysangiz, IDE avtomatik to'ldirish orqali shu domenning so'rovlar ro'yxati to'liq ko'rsatiladi.

// 특정 쿼리 하나만 무효화
queryClient.invalidateQueries({
  queryKey: productCmKeys.projectHealthSummaries({ productId }),
});
 
// product 도메인 전체 무효화
queryClient.invalidateQueries({ queryKey: productCmKeys.all() });
 
// mutation 성공 후 여러 도메인 무효화
onSuccess: () => {
  queryClient.invalidateQueries({ queryKey: productCmKeys.all() });
  queryClient.invalidateQueries({ queryKey: teamCmKeys.all() });
},

4. Papka tuzilmasi va yakunlab

Papka tuzilmasi: domen bo'yicha ajratish

Ushbu shablonni qo'llash natijasida tabiiy ravishda quyidagi papka tuzilmasi paydo bo'ladi.

feature/
├── product/
│   └── cm/
│       ├── queries/
│       │   ├── product.cm.keys.ts          ← 키 factory
│       │   └── findProductLookups.query.ts  ← queryOptions 함수
│       ├── hooks/
│       │   └── useFindProductLookups.ts     ← 훅
│       └── index.ts
├── team/
└── staffing/
 ...

*.cm.keys.ts ushbu domenning kesh tuzilmasini to'liq o'z ichiga oluvchi hujjatga aylanadi. Yangi so'rov qo'shilganda, bu yerga bitta kalit funktsiyasini qo'shamiz va *.query.ts da ushbu kalitni ishora qilamiz.

Xulosa qilib aytganda

Query Key Factory shablonini bir qator bilan ta'riflasak, so'rov kalitini funktsiya sifatida yaratib, bitta joyda boshqarish va kesh qatlamini kod bilan to'g'ridan-to'g'ri ifodalashdir.

Boshida "Qanday qilib shuncha bormoqda?" degan fikr bo'lishi mumkin. Men ham shunday o'yladim. Lekin doimiylik oshgani sayin, jamoa a'zolari ko'paygani sayin ta'sir aniq ko'zga tashlanadi. Kalitlarni satr sifatida har yerga tarqatib yuborish, keyinchalik kesh tuzilmasini tushunishni tobora qiyinlashtiradi va invalidatsiya kodi esa "Bu joyi yetmayapti?" degan noaniq qo'l mehnatiga aylanadi. Fabrikada boshqarilganda *.keys.ts faylini ochish orqali butun tuzilmani bir qarashda ko'rishingiz mumkin.

walrus

Site footer