I'm building an online store in React and decided to use React Query for the project. The task is to display and store products in the cache. From these products, I need to fetch the required one on the product page and render it. There are two scenarios:
- The product is in the cache - I retrieve it from the cache and return it from the function.
- It's not in the cache - I fetch it from the server, load it into the cache, and return it from the function. The problem is that I don't understand how to update the data in the cache.
Here's my custom hook:
// useProducts.ts
import { IProduct } from '@/interfaces/product.interface';
import ProductService from '@/services/product/product.service';
import { useQuery, useQueryClient } from '@tanstack/react-query';
export const useProducts = () => {
const queryClient = useQueryClient();
const { data, isSuccess } = useQuery({
queryKey: ['products'],
queryFn: () => ProductService.getAll({ page: 1, perPage: 9 }),
select: ({ data }) => data,
});
const getProductById = async (productId: number) => {
console.log("cached products: ", queryClient.getQueryCache().find({ queryKey: ['products'] })?.state.data?.data);
const product = data?.products.find((p) => p.id === productId);
if (!!product) return product;
try {
const response = await ProductService.getById(productId);
queryClient.setQueryData<IProduct[]>(['products'], (state) => {
console.log("state:", state);
return [...(state || []), response];
});
return response;
} catch (error) {
console.error('Error fetching product by ID:', error);
return null;
}
};
return { data, isSuccess, getProductById };
};
// ProductService.ts
import { $axios } from '@/api/api.interceptor';
import { IProduct } from '@/interfaces/product.interface';
import {
IProductResponse,
ProductDataFiltersType,
ProductDataType,
} from './product.types';
class ProductService {
private url = '/product';
getAll = async (queryData = {} as ProductDataFiltersType) => {
return $axios.get<IProductResponse>(this.url, { params: queryData });
};
getById = async (id: string | number) => {
// return $axios.get<IProduct>(`${this.url}/${id}`);
const response = await $axios.get<IProduct>(`${this.url}/${id}`);
return response.data;
};
...
}
export default new ProductService();
export type IProductResponse = {
products: IProduct[]
currentPage: number
totalPages: number
}
For some reason, when I call setQueryData, the axios response is in the state instead of the array of products (as I think it should work):
cached products: {products: Array(9), totalPages: 2, currentPage: 1}
state: {data: {products, currentPage, totalPages}, status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …}
Consequently, my error is -
useProducts.ts:42 Error fetching product by ID: TypeError: (state || []) is not iterable
at useProducts.ts:25:21
at functionalUpdate (@tanstack_react-query.js?v=6de31b48:42:42)
at _a8.setQueryData (@tanstack_react-query.js?v=6de31b48:1538:18)
at getProductById (useProducts.ts:24:19)
Btw, products in my Home.tsx works perfectly.
import CardsContainer from '@/components/shared/CardsContainer/CardsContainer';
import { useProducts } from '@/hooks/features/products/useProducts';
import { useActions } from '@/hooks/general/useActions';
import { useEffect } from 'react';
import Banner from './components/Banner/Banner';
function Home() {
const { data, isSuccess } = useProducts();
const { checkAuth } = useActions();
useEffect(() => {
checkAuth();
}, []);
return (
<section className="rows-container">
<Banner />
{isSuccess && data?.products && (
<CardsContainer products={data.products} />
)}
</section>
);
}
export default Home;
P.S.
- Can you explain to a newbie the difference between setQueryData and setQueriesData?
- Is there a way for me to see the data that React Query stores in the cache (Maybe there are some extensions)?