"The best abstractions hide complexity while exposing power." This principle led us to create a hook pattern that eliminated thousands of lines of code while making our application smarter.
Picture this: You have testimonials on every page of your application. But each page needs different testimonials:
- GenAI page: AI success stories
- Mobile app page: App store reviews
- GIS page: Mapping project testimonials
- ERP page: Integration case studies
Traditional solution: Create 23 separate testimonial components.
Our solution: One hook that automatically knows what to fetch.
const { data } = useTestimonials(pagelink);
This single line replaced 8,500 lines of duplicate code and revolutionized how we handle multi-context applications.
Table of Contents
- The Multi-Context Hell
- The Breakthrough Pattern
- Deep Dive: Hook Mapping Architecture
- Advanced Patterns
- Type Safety Across Contexts
- Performance Optimizations
- Real-World Battle Stories
- The Results
The Multi-Context Hell {#the-problem}
Multiple business verticals through one platform. Each vertical needs the same UI components but with different data sources.
The Traditional Nightmare
// Before: Separate component for each context const GenAiTestimonials = () => { const { data } = useQuery('/api/genai-testimonials', transformGenAiTestimonials); return <TestimonialCarousel data={data} />; }; const MobileAppTestimonials = () => { const { data } = useQuery('/api/mobile-testimonials', transformMobileTestimonials); return <TestimonialCarousel data={data} />; }; const GISTestimonials = () => { const { data } = useQuery('/api/gis-testimonials', transformGISTestimonials); return <TestimonialCarousel data={data} />; }; // ... 20 more similar components
Problems everywhere:
- π΄ 23 duplicate testimonial components
- π΄ Inconsistent implementations
- π΄ Update one = update all
- π΄ Testing nightmare (23 Γ test cases)
- π΄ Bundle size explosion
The Prop Drilling Solution (Also Terrible)
// Slightly better, but still painful const TestimonialsSection = ({ apiEndpoint, transformFunction, cacheKey, retryCount, staleTime, errorMessage }) => { const { data } = useQuery({ queryKey: [cacheKey], queryFn: () => fetch(apiEndpoint).then(transformFunction), retry: retryCount, staleTime }); if (!data) return <div>{errorMessage}</div>; return <TestimonialCarousel data={data} />; }; // Parent component nightmare <TestimonialsSection apiEndpoint="/api/genai-testimonials" transformFunction={transformGenAiTestimonials} cacheKey="genai-testimonials" retryCount={3} staleTime={300000} errorMessage="Failed to load GenAI testimonials" />
Components shouldn't care about API endpoints, cache keys, or transformation functions!
The Breakthrough Pattern {#the-breakthrough}
What if components could automatically get the right data based on context?
// After: One component, infinite contexts const TestimonialsSection = ({ pagelink }) => { const { data, isLoading, error } = useTestimonials(pagelink); if (isLoading) return <TestimonialSkeleton />; if (error) return <TestimonialError onRetry={refetch} />; return <TestimonialCarousel data={data} />; }; // Usage - component automatically knows what to fetch <TestimonialsSection pagelink="genai" /> // Gets GenAI testimonials <TestimonialsSection pagelink="mobile-app" /> // Gets mobile testimonials <TestimonialsSection pagelink="gis" /> // Gets GIS testimonials
The magic happens in the hook:
const useTestimonials = createHookMap({ 'genai': useGenAiTestimonials, 'mobile-app': useMobileAppTestimonials, 'gis': useGISTestimonials, 'erp': useERPTestimonials, 'default': useGeneralTestimonials }, 'default');
Deep Dive: Hook Mapping Architecture {#architecture}
The Core Pattern
type HookMap<T> = Record<string, () => UseQueryResult<T>>; type HookSelector<T> = (context?: string) => UseQueryResult<T>; export function createHookMap<T>( hookMap: HookMap<T>, defaultKey: string ): HookSelector<T> { return (context?: string): UseQueryResult<T> => { // Smart hook selection with fallback strategy const selectedHook = context && hookMap[context] ? hookMap[context] : hookMap[defaultKey]; if (!selectedHook) { console.warn(`No hook found for context: ${context}, falling back to default`); // Return controlled error state instead of crashing return { data: undefined, isLoading: false, isError: true, error: new Error(`Invalid context: ${context}`), refetch: () => Promise.resolve({ data: undefined } as any), // ... other UseQueryResult properties } as UseQueryResult<T>; } return selectedHook(); }; }
Advanced Multi-Level Context Resolution
For complex applications, we support nested contexts:
export function createNestedHookMap<T>( hookMap: Record<string, Record<string, () => UseQueryResult<T>>>, defaultPath: string ): HookSelector<T> { return (contextPath?: string): UseQueryResult<T> => { if (!contextPath) { return resolveDefaultHook(hookMap, defaultPath); } const [context, subContext] = contextPath.split('.'); // Resolution priority: // 1. Exact match: 'genai.document-analyzer' // 2. Context default: 'genai.default' // 3. Global default: 'default.default' if (hookMap[context]?.[subContext]) { return hookMap[context][subContext](); } if (hookMap[context]?.['default']) { return hookMap[context]['default'](); } return resolveDefaultHook(hookMap, defaultPath); }; } // Usage example export const useProcessingMode = createNestedHookMap({ 'genai': { 'document-analyzer': useDocumentAnalyzerModes, 'chat-interface': useChatInterfaceModes, 'image-processor': useImageProcessorModes, 'default': useGenAiProcessingModes }, 'mobile-app': { 'ios': useIOSProcessingModes, 'android': useAndroidProcessingModes, 'cross-platform': useCrossPlatformModes, 'default': useMobileProcessingModes } }, 'genai.default'); // Component usage const { data } = useProcessingMode('genai.document-analyzer');
Real-World Hook Library
Here's our actual production hook mapping:
// API hook library export const useHeroSection = createHookMap({ 'home': useHomeHero, 'genai': useGenAiHero, 'mobile-app': useMobileAppHero, 'gis-solutions': useGISHero, 'erp': useERPHero, 'about': useAboutHero, 'contact': useContactHero, 'services': useServicesHero }, 'home'); export const useTestimonials = createHookMap({ 'genai': useGenAiTestimonials, 'mobile-app': useMobileAppTestimonials, 'gis-solutions': useGISTestimonials, 'erp': useERPTestimonials, 'services': useServicesTestimonials, 'default': useGeneralTestimonials }, 'default'); export const useFeatures = createHookMap({ 'genai': useGenAiFeatures, 'mobile-app': useMobileAppFeatures, 'gis-solutions': useGISFeatures, 'erp': useERPFeatures, 'default': useGeneralFeatures }, 'default'); export const usePricing = createHookMap({ 'genai': useGenAiPricing, 'mobile-app': useMobileAppPricing, 'enterprise': useEnterprisePricing, 'default': useStandardPricing }, 'default');
Advanced Patterns {#advanced-patterns}
Conditional Hook Mapping
// Dynamic hook selection based on user permissions export const useRestrictedContent = createConditionalHookMap({ conditions: [ { when: (user) => user?.role === 'admin', hookMap: { 'genai': useAdminGenAiContent, 'mobile-app': useAdminMobileContent } }, { when: (user) => user?.subscription === 'premium', hookMap: { 'genai': usePremiumGenAiContent, 'mobile-app': usePremiumMobileContent } } ], fallback: { 'genai': usePublicGenAiContent, 'mobile-app': usePublicMobileContent } }); // Usage with automatic permission handling const ContentSection = ({ pagelink }) => { const user = useCurrentUser(); const { data } = useRestrictedContent(pagelink, user); return <ContentDisplay data={data} />; };
Hook Composition Patterns
// Combine multiple context-aware hooks export const usePageData = (pagelink: string) => { const hero = useHeroSection(pagelink); const testimonials = useTestimonials(pagelink); const features = useFeatures(pagelink); const pricing = usePricing(pagelink); // Smart loading state - show content as it becomes available const loadingStates = [hero, testimonials, features, pricing]; const isLoading = loadingStates.every(state => state.isLoading); const hasError = loadingStates.some(state => state.error); return { data: { hero: hero.data, testimonials: testimonials.data, features: features.data, pricing: pricing.data }, isLoading, hasError, refetchAll: () => Promise.all(loadingStates.map(state => state.refetch)) }; };
Cache Strategies Per Context
// Different caching strategies for different contexts const createContextAwareHook = (endpoint: string, transform: Function) => { return () => useQuery({ queryKey: [endpoint], queryFn: () => fetch(`/api${endpoint}`).then(transform), // Context-specific caching staleTime: getCacheTime(endpoint), cacheTime: getRetentionTime(endpoint), refetchOnWindowFocus: shouldRefetchOnFocus(endpoint), retry: getRetryStrategy(endpoint) }); }; const getCacheTime = (endpoint: string): number => { const strategies = { '/testimonials': 10 * 60 * 1000, // 10 minutes - changes rarely '/pricing': 60 * 60 * 1000, // 1 hour - very stable '/user-dashboard': 30 * 1000, // 30 seconds - personal data '/real-time-data': 0, // No caching - always fresh }; return strategies[endpoint] || 5 * 60 * 1000; // Default 5 minutes };
Type Safety Across Contexts {#type-safety}
Our pattern maintains complete type safety across all contexts:
// Shared data types interface TestimonialData { id: string; name: string; role: string; company: string; quote: string; rating: number; image?: string; featured: boolean; source: string; // Context tracking } // Context-specific transformers with validation export const transformGenAiTestimonials = ( data: unknown ): TestimonialData[] => { // Runtime type validation if (!isValidTestimonialResponse(data)) { logger.error('Invalid GenAI testimonial data', { data }); return getFallbackTestimonials('genai'); } const apiData = data as GenAiTestimonialApiResponse; return apiData.data.testimonials.map(item => ({ id: item.id.toString(), name: sanitizeString(item.name), role: sanitizeString(item.role), company: sanitizeString(item.company), quote: sanitizeString(item.quote), rating: Math.min(5, Math.max(0, item.rating || 5)), image: buildImageUrl(item.image?.url) || getDefaultAvatar(item.name), featured: Boolean(item.featured), source: 'genai' })); }; // Type guard for runtime safety function isValidTestimonialResponse(data: unknown): boolean { if (!data || typeof data !== 'object') return false; const obj = data as any; return ( 'data' in obj && 'testimonials' in obj.data && Array.isArray(obj.data.testimonials) && obj.data.testimonials.every((t: any) => typeof t.id !== 'undefined' && typeof t.name === 'string' && typeof t.quote === 'string' ) ); } // Full TypeScript inference in components const TestimonialsSection = ({ pagelink }: { pagelink: string }) => { const { data } = useTestimonials(pagelink); // ^? TestimonialData[] | undefined return ( <div> {data?.map(testimonial => ( <TestimonialCard key={testimonial.id} name={testimonial.name} // β
Fully typed company={testimonial.company} // β
Fully typed rating={testimonial.rating} // β
Fully typed /> ))} </div> ); };
Performance Optimizations {#performance}
Intelligent Caching Strategy
// Shared cache optimization const useSharedTestimonials = () => { return useQuery({ queryKey: ['testimonials', 'shared'], queryFn: fetchSharedTestimonials, staleTime: 15 * 60 * 1000, // 15 minutes cacheTime: 60 * 60 * 1000, // 1 hour retention }); }; // Context-specific with fallback to shared const useContextTestimonials = (pagelink: string) => { const sharedQuery = useSharedTestimonials(); const contextQuery = useQuery({ queryKey: ['testimonials', pagelink], queryFn: () => fetchContextTestimonials(pagelink), enabled: !!pagelink && pagelink !== 'default', staleTime: 5 * 60 * 1000, }); // Return context-specific if available, otherwise shared if (contextQuery.data && contextQuery.data.length > 0) { return contextQuery; } return sharedQuery; };
Prefetching Strategies
// Predictive prefetching based on user navigation patterns export const usePrefetchStrategy = (currentPagelink: string) => { const queryClient = useQueryClient(); useEffect(() => { const prefetchTargets = getPrefetchTargets(currentPagelink); // Prefetch likely next pages prefetchTargets.forEach(target => { queryClient.prefetchQuery({ queryKey: ['testimonials', target], queryFn: () => fetchTestimonials(target), staleTime: 10 * 60 * 1000 }); }); // Prefetch related contexts const relatedContexts = getRelatedContexts(currentPagelink); relatedContexts.forEach(context => { setTimeout(() => { queryClient.prefetchQuery({ queryKey: ['features', context], queryFn: () => fetchFeatures(context) }); }, 2000); // Delayed prefetch }); }, [currentPagelink, queryClient]); }; // Smart prefetch targets based on analytics const getPrefetchTargets = (current: string): string[] => { const navigationPatterns = { 'home': ['genai', 'mobile-app', 'about'], 'genai': ['genai-chatbot', 'about', 'contact'], 'mobile-app': ['services', 'pricing', 'contact'], }; return navigationPatterns[current] || []; };
Real-World Battle Stories {#battle-stories}
Case Study 1: The Testimonial Explosion
Problem: 23 testimonial components across the platform, each with subtle differences.
Before:
components/testimonials/ βββ GenAiTestimonials.tsx (245 lines) βββ MobileAppTestimonials.tsx (267 lines) βββ GISTestimonials.tsx (234 lines) βββ ERPTestimonials.tsx (289 lines) βββ ServicesTestimonials.tsx (198 lines) ... 18 more files Total: 5,670 lines of code
After:
components/testimonials/ βββ TestimonialsSection.tsx (89 lines) hooks/ βββ useTestimonials.ts (34 lines) config/ βββ testimonialHooks.ts (67 lines) Total: 190 lines of code
Impact: 96.6% code reduction, 100% feature parity
Case Study 2: The Feature Matrix Problem
Challenge: Different features for different product pages, with complex conditional logic.
Old approach:
const FeatureSection = ({ page, userTier, region, experiment }) => { const [features, setFeatures] = useState([]); useEffect(() => { let endpoint = '/api/features'; if (page === 'genai') { endpoint = userTier === 'premium' ? '/api/genai-premium-features' : '/api/genai-basic-features'; } else if (page === 'mobile-app') { endpoint = region === 'US' ? '/api/mobile-us-features' : '/api/mobile-global-features'; } // ... 50 more lines of conditional logic fetchFeatures(endpoint).then(setFeatures); }, [page, userTier, region, experiment]); // Complex rendering logic... };
New approach:
const FeatureSection = ({ pagelink }) => { const user = useCurrentUser(); const { data } = useFeatures(pagelink, user); return <FeatureGrid features={data} />; }; // All complexity moved to hook configuration const useFeatures = createConditionalHookMap({ contexts: ['genai', 'mobile-app', 'gis', 'erp'], conditions: [ { when: isPremiumUser, hookSuffix: '-premium' }, { when: isUSRegion, hookSuffix: '-us' }, { when: isExperimentB, hookSuffix: '-experiment-b' } ] });
Case Study 3: The Dashboard Data Nightmare
Scenario: User dashboard showing different widgets based on user's subscribed services.
Before: 15 different dashboard components, massive prop drilling
After: One dashboard component with context-aware widgets
const DashboardPage = ({ userId }) => { const user = useUserProfile(userId); const services = user?.subscribedServices || []; return ( <Dashboard> {services.map(service => ( <DashboardWidget key={service} type="metrics" context={service} /> ))} </Dashboard> ); }; // Widget automatically knows what metrics to show const DashboardWidget = ({ type, context }) => { const { data } = useDashboardData(`${context}.${type}`); return <MetricsWidget data={data} />; };
The Results {#results}
After implementing context-aware hooks across our entire platform:
Code Metrics
Metric | Before | After | Improvement |
---|---|---|---|
Testimonial Components | 23 | 1 | -95.7% |
Feature Components | 18 | 1 | -94.4% |
Total Lines of Code | 37,500 | 29,000 | -22.7% |
Duplicate Code | 8,500 lines | 0 lines | -100% |
Bundle Size | +15% (per context) | -12% (overall) | -27% |
Developer Experience
- Time to add new context: 4 hours β 15 minutes (-93.8%)
- Bug reports from data issues: 23/month β 3/month (-87%)
- Code review time: 45 min β 8 min (-82%)
- Developer satisfaction: "Working with APIs" went from 4.2/10 β 8.7/10
Performance Impact
- API calls reduced: 40% fewer requests (intelligent caching)
- Bundle size: 27% smaller (no duplicate components)
- Memory usage: 35% less (shared component instances)
- Cache hit rate: 78% (context-aware caching strategies)
Business Impact
- Feature delivery velocity: 3x faster
- Cross-team collaboration: Improved (standardized patterns)
- Bug fix time: 60% reduction (centralized logic)
- New developer onboarding: 2 weeks β 3 days
Advanced Use Cases
Multi-Tenant SaaS Applications
// Perfect for white-label solutions const useBrandedContent = createHookMap({ 'client-a': useClientAContent, 'client-b': useClientBContent, 'client-c': useClientCContent, 'default': useDefaultContent }, 'default'); // Component works across all tenants const BrandedHero = () => { const tenant = useTenant(); const { data } = useBrandedContent(tenant.slug); return <HeroSection {...data} />; };
E-commerce Category Pages
// One product listing, all categories const useProducts = createNestedHookMap({ 'electronics': { 'phones': usePhoneProducts, 'laptops': useLaptopProducts, 'default': useElectronicsProducts }, 'fashion': { 'men': useMensFashion, 'women': useWomensFashion, 'default': useFashionProducts } }, 'electronics.default'); // Usage: useProducts('fashion.women')
Content Management Systems
// Dynamic page builder const usePageContent = createHookMap({ 'blog': useBlogContent, 'landing': useLandingContent, 'product': useProductContent, 'custom': useCustomContent }, 'custom'); const DynamicPage = ({ pageType, pageId }) => { const { data } = usePageContent(`${pageType}/${pageId}`); return <PageBuilder blocks={data.blocks} />; };
Best Practices We've Learned
1. Design Hooks for Composition
// Good: Composable hooks const usePageData = (context: string) => { const hero = useHero(context); const features = useFeatures(context); const testimonials = useTestimonials(context); return { hero, features, testimonials }; }; // Bad: Monolithic hook const useAllPageData = (context: string) => { // Fetches everything in one giant request };
2. Implement Graceful Degradation
const createResilientHookMap = <T>(hookMap: HookMap<T>, defaultKey: string) => { return (context?: string) => { try { const hook = hookMap[context] || hookMap[defaultKey]; const result = hook(); // If primary source fails, try fallback if (result.error && context !== defaultKey) { return hookMap[defaultKey](); } return result; } catch (error) { console.error('Hook execution failed:', error); return createErrorState<T>(error); } }; };
3. Monitor Hook Performance
// Track hook usage and performance const withMetrics = <T>(hookName: string, hook: () => UseQueryResult<T>) => { return () => { const startTime = performance.now(); const result = hook(); const endTime = performance.now(); // Track execution time analytics.track('hook_performance', { hookName, executionTime: endTime - startTime, cacheHit: !result.isFetching && !!result.data, error: !!result.error }); return result; }; };
Conclusion
Context-aware API hooks represent a fundamental shift in how we think about data fetching in React applications. By moving context awareness into the hook layer, we've achieved:
- Radical Code Reduction: 95%+ elimination of duplicate components
- Type Safety: End-to-end TypeScript coverage
- Performance: Intelligent caching and prefetching
- Developer Experience: Simple mental model, powerful abstractions
- Maintainability: Centralized logic, easier testing
The pattern scales from simple use cases to complex multi-tenant applications. Start with one context-aware hook, measure the impact, then expand.
Remember: The best abstractions hide complexity while exposing power. Context-aware hooks do exactly that.
Resources
- GitHub: Context-Aware Hook Examples
- Demo: Live Implementation
- TypeScript Definitions: Hook Mapping Types
Connect with me:
- LinkedIn: linkedin.com/in/maurya-sachin
- Portfolio: sachin-gilt.vercel.app
- Email: sachinmaurya1710@gmail.com
Have you implemented similar patterns? What challenges did you face? Share your experience in the comments!
Top comments (0)