useQueryClient Hook
The useQueryClient hook provides access to the global QueryClient instance for manual cache management and query operations.
Basic Usage
Section titled “Basic Usage”import 'package:flutter_hooks/flutter_hooks.dart';import 'package:fasq_hooks/fasq_hooks.dart';
class CacheManager extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
return Column( children: [ ElevatedButton( onPressed: () { // Invalidate specific query queryClient.invalidateQuery('users'); }, child: Text('Invalidate Users'), ), ElevatedButton( onPressed: () { // Set query data manually queryClient.setQueryData('user:1', User(id: '1', name: 'John')); }, child: Text('Set User Data'), ), ElevatedButton( onPressed: () { // Get cache info final info = queryClient.getCacheInfo(); print('Cache entries: ${info.entryCount}'); print('Hit rate: ${info.metrics.hitRate * 100}%'); }, child: Text('Print Cache Info'), ), ], ); }}Cache Invalidation
Section titled “Cache Invalidation”Invalidate queries to force refetch:
class RefreshButton extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
return ElevatedButton( onPressed: () { // Invalidate specific query queryClient.invalidateQuery('users');
// Invalidate multiple queries with prefix queryClient.invalidateQueriesWithPrefix('user:');
// Invalidate with custom logic queryClient.invalidateQueriesWhere((key) => key.contains('stale')); }, child: Text('Refresh All'), ); }}Manual Cache Updates
Section titled “Manual Cache Updates”Set cache data manually for optimistic updates:
class OptimisticUpdate extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
return ElevatedButton( onPressed: () { // Get current data final users = queryClient.getQueryData<List<User>>('users');
// Create optimistic update final optimisticUsers = [ ...users ?? [], User(id: 'new', name: 'New User'), ];
// Set optimistic data queryClient.setQueryData('users', optimisticUsers);
// Make API call api.createUser({'name': 'New User'}).then((newUser) { // Update with real data queryClient.setQueryData('users', [ ...users ?? [], newUser, ]); }).catchError((error) { // Rollback on error queryClient.setQueryData('users', users); }); }, child: Text('Add User Optimistically'), ); }}Query Management
Section titled “Query Management”Manage queries directly:
class QueryManager extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
return Column( children: [ ElevatedButton( onPressed: () { // Get specific query final query = queryClient.getQueryByKey<List<User>>('users'); query?.fetch(); // Manual refetch }, child: Text('Refetch Users'), ), ElevatedButton( onPressed: () { // Check if query exists final exists = queryClient.hasQuery('users'); print('Users query exists: $exists'); }, child: Text('Check Query Exists'), ), ElevatedButton( onPressed: () { // Remove specific query queryClient.removeQuery('users'); }, child: Text('Remove Users Query'), ), ElevatedButton( onPressed: () { // Clear all queries queryClient.clear(); }, child: Text('Clear All'), ), ], ); }}Cache Information
Section titled “Cache Information”Get detailed cache information:
class CacheInfo extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
return ElevatedButton( onPressed: () { final info = queryClient.getCacheInfo();
print('Cache Statistics:'); print('- Total entries: ${info.entryCount}'); print('- Cache size: ${info.sizeBytes} bytes'); print('- Hit rate: ${(info.metrics.hitRate * 100).toStringAsFixed(1)}%'); print('- Hits: ${info.metrics.hits}'); print('- Misses: ${info.metrics.misses}');
// Get specific query data final users = queryClient.getQueryData<List<User>>('users'); if (users != null) { print('- Users in cache: ${users.length}'); } }, child: Text('Print Cache Stats'), ); }}Prefetching
Section titled “Prefetching”Prefetch queries for better performance:
class PrefetchExample extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
return ElevatedButton( onPressed: () { // Prefetch users data queryClient.prefetchQuery( 'users', () => api.fetchUsers(), );
// Prefetch specific user queryClient.prefetchQuery( 'user:123', () => api.fetchUser('123'), ); }, child: Text('Prefetch Data'), ); }}Conditional Operations
Section titled “Conditional Operations”Perform operations based on cache state:
class ConditionalOperations extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
return ElevatedButton( onPressed: () { // Only invalidate if data exists final users = queryClient.getQueryData<List<User>>('users'); if (users != null) { queryClient.invalidateQuery('users'); }
// Only prefetch if not already cached if (!queryClient.hasQuery('posts')) { queryClient.prefetchQuery('posts', () => api.fetchPosts()); } }, child: Text('Conditional Operations'), ); }}Error Recovery
Section titled “Error Recovery”Handle cache errors and recovery:
class ErrorRecovery extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
return ElevatedButton( onPressed: () { try { // Attempt to get cached data final users = queryClient.getQueryData<List<User>>('users');
if (users == null) { // Data not in cache, fetch it queryClient.prefetchQuery('users', () => api.fetchUsers()); } } catch (error) { // Handle cache errors print('Cache error: $error');
// Clear problematic cache queryClient.removeQuery('users');
// Refetch fresh data queryClient.prefetchQuery('users', () => api.fetchUsers()); } }, child: Text('Error Recovery'), ); }}Performance Monitoring
Section titled “Performance Monitoring”Monitor cache performance:
class PerformanceMonitor extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
return ElevatedButton( onPressed: () { final info = queryClient.getCacheInfo();
// Check cache efficiency if (info.metrics.hitRate < 0.5) { print('Warning: Low cache hit rate (${(info.metrics.hitRate * 100).toStringAsFixed(1)}%)'); }
// Check memory usage if (info.sizeBytes > 10 * 1024 * 1024) { // 10MB print('Warning: High cache memory usage (${info.sizeBytes} bytes)'); }
// Log performance metrics print('Cache Performance:'); print('- Hit rate: ${(info.metrics.hitRate * 100).toStringAsFixed(1)}%'); print('- Memory usage: ${(info.sizeBytes / 1024 / 1024).toStringAsFixed(1)}MB'); print('- Active queries: ${info.entryCount}'); }, child: Text('Monitor Performance'), ); }}Type Safety
Section titled “Type Safety”Full generic type support ensures compile-time safety:
class TypeSafeOperations extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
return ElevatedButton( onPressed: () { // Type-safe data access final users = queryClient.getQueryData<List<User>>('users'); // users is List<User>?
// Type-safe data setting queryClient.setQueryData<List<User>>('users', [ User(id: '1', name: 'John'), User(id: '2', name: 'Jane'), ]);
// Type-safe query access final query = queryClient.getQueryByKey<List<User>>('users'); // query is Query<List<User>>? }, child: Text('Type Safe Operations'), ); }}Common Patterns
Section titled “Common Patterns”Cache Warming
Section titled “Cache Warming”class CacheWarmer extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
useEffect(() { // Warm cache on mount queryClient.prefetchQuery('users', () => api.fetchUsers()); queryClient.prefetchQuery('posts', () => api.fetchPosts());
return null; }, []);
return Text('Cache warming...'); }}Cache Cleanup
Section titled “Cache Cleanup”class CacheCleanup extends HookWidget { @override Widget build(BuildContext context) { final queryClient = useQueryClient();
useEffect(() { return () { // Cleanup on unmount queryClient.invalidateQueriesWithPrefix('temp:'); }; }, []);
return ElevatedButton( onPressed: () { // Create temporary data queryClient.setQueryData('temp:data', {'temp': true}); }, child: Text('Create Temp Data'), ); }}Performance Tips
Section titled “Performance Tips”- Use prefetching - Load data before it’s needed
- Monitor cache performance - Track hit rates and memory usage
- Invalidate strategically - Only invalidate what’s necessary
- Use type-safe operations - Leverage compile-time safety
- Handle errors gracefully - Implement proper error recovery
Next Steps
Section titled “Next Steps”- useQuery - Learn about queries
- useMutation - Learn about mutations
- Custom Hooks - Creating reusable logic
- Examples - Complete working examples