BigBlocks Docs
Components/Providers

BitcoinQueryProvider

React Query provider for efficient data caching and state management in Bitcoin applications

A React Query-based provider that enables efficient data caching, synchronization, and state management for BigBlocks components. It provides optimized caching for market data, wallet operations, and social features.

Installation

npx bigblocks add bitcoin-query-provider

Import

import { BitcoinQueryProvider } from 'bigblocks';

Props

PropTypeRequiredDefaultDescription
childrenReact.ReactNodeYes-Child components
queryClientQueryClientNoDefault clientCustom React Query client

Basic Usage

import { BitcoinQueryProvider } from 'bigblocks';

function App() {
  return (
    <BitcoinQueryProvider>
      <BitcoinThemeProvider>
        <BitcoinAuthProvider config={{ apiUrl: '/api' }}>
          <YourAppContent />
        </BitcoinAuthProvider>
      </BitcoinThemeProvider>
    </BitcoinQueryProvider>
  );
}

Advanced Usage

Custom Query Client

import { BitcoinQueryProvider } from 'bigblocks';
import { QueryClient } from '@tanstack/react-query';

export default function CustomQueryApp() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 5 * 60 * 1000,    // 5 minutes
        cacheTime: 10 * 60 * 1000,   // 10 minutes
        refetchOnWindowFocus: false,
        retry: (failureCount, error) => {
          if (error.status === 401) return false;
          return failureCount < 3;
        },
      },
      mutations: {
        retry: 1,
      },
    },
  });

  return (
    <BitcoinQueryProvider queryClient={queryClient}>
      <App />
    </BitcoinQueryProvider>
  );
}

Production Configuration

import { BitcoinQueryProvider } from 'bigblocks';
import { QueryClient } from '@tanstack/react-query';

export default function ProductionApp() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 2 * 60 * 1000,      // 2 minutes
        cacheTime: 15 * 60 * 1000,     // 15 minutes
        refetchOnWindowFocus: true,
        refetchOnReconnect: true,
        networkMode: 'offlineFirst',
        retry: (failureCount, error) => {
          // Don't retry client errors
          if (error.status >= 400 && error.status < 500) {
            return false;
          }
          return failureCount < 2;
        },
      },
    },
  });

  // Set specific defaults for different query types
  queryClient.setQueryDefaults(['balance'], {
    staleTime: 30 * 1000,              // Balance data should be fresh
    refetchInterval: 60 * 1000,        // Auto-refresh every minute
  });

  queryClient.setQueryDefaults(['market'], {
    staleTime: 5 * 60 * 1000,          // Market data can be cached longer
  });

  return (
    <BitcoinQueryProvider queryClient={queryClient}>
      <App />
    </BitcoinQueryProvider>
  );
}

With Error Handling

import { BitcoinQueryProvider } from 'bigblocks';
import { QueryClient, QueryCache } from '@tanstack/react-query';

export default function ErrorHandlingApp() {
  const queryClient = new QueryClient({
    queryCache: new QueryCache({
      onError: (error) => {
        console.error('Query error:', error);
        if (error.status === 401) {
          // Handle authentication errors
          window.location.href = '/signin';
        }
      },
    }),
    defaultOptions: {
      mutations: {
        onError: (error) => {
          console.error('Mutation failed:', error);
          // Show error notification
          toast.error('Operation failed. Please try again.');
        },
      },
    },
  });

  return (
    <BitcoinQueryProvider queryClient={queryClient}>
      <App />
    </BitcoinQueryProvider>
  );
}

With DevTools

import { BitcoinQueryProvider } from 'bigblocks';
import { QueryClient } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

export default function DevelopmentApp() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 0,                  // Always refetch in development
        refetchOnWindowFocus: false,   // Disable for development
      },
    },
  });

  return (
    <BitcoinQueryProvider queryClient={queryClient}>
      <App />
      {process.env.NODE_ENV === 'development' && (
        <ReactQueryDevtools initialIsOpen={false} />
      )}
    </BitcoinQueryProvider>
  );
}

Common Patterns

Provider Nesting Order

import { 
  BitcoinQueryProvider,
  BitcoinThemeProvider,
  BitcoinAuthProvider 
} from 'bigblocks';

// Correct nesting order (outside to inside):
// 1. BitcoinQueryProvider (provides React Query context)
// 2. BitcoinThemeProvider (provides theme context)
// 3. BitcoinAuthProvider (uses React Query for auth state)

export default function App() {
  return (
    <BitcoinQueryProvider>
      <BitcoinThemeProvider defaultTheme="orange">
        <BitcoinAuthProvider config={{ apiUrl: '/api' }}>
          <YourApp />
        </BitcoinAuthProvider>
      </BitcoinThemeProvider>
    </BitcoinQueryProvider>
  );
}

Memory Optimization

import { BitcoinQueryProvider } from 'bigblocks';
import { QueryClient } from '@tanstack/react-query';
import { useEffect } from 'react';

export default function MemoryOptimizedApp() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        cacheTime: 5 * 60 * 1000,      // Shorter cache time
        staleTime: 1 * 60 * 1000,      // Data stales quickly
        refetchOnMount: 'always',      // Always fetch fresh data
      },
    },
  });

  // Clear cache on logout
  useEffect(() => {
    const handleLogout = () => {
      queryClient.clear();
    };

    window.addEventListener('logout', handleLogout);
    return () => window.removeEventListener('logout', handleLogout);
  }, [queryClient]);

  return (
    <BitcoinQueryProvider queryClient={queryClient}>
      <App />
    </BitcoinQueryProvider>
  );
}

With Suspense

import { BitcoinQueryProvider } from 'bigblocks';
import { QueryClient } from '@tanstack/react-query';
import { Suspense } from 'react';

export default function SuspenseApp() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        suspense: true,                // Enable suspense mode
        useErrorBoundary: true,        // Use error boundaries
      },
    },
  });

  return (
    <BitcoinQueryProvider queryClient={queryClient}>
      <ErrorBoundary fallback={<ErrorPage />}>
        <Suspense fallback={<LoadingSpinner />}>
          <App />
        </Suspense>
      </ErrorBoundary>
    </BitcoinQueryProvider>
  );
}

Benefits

1. Request Deduplication

Multiple components requesting the same data will result in a single network request:

// Both components will share the same query
function ComponentA() {
  const { data } = useQuery({ queryKey: ['market'] });
  return <div>{data}</div>;
}

function ComponentB() {
  const { data } = useQuery({ queryKey: ['market'] });
  return <div>{data}</div>;
}

2. Background Updates

Data is refetched in the background when it becomes stale:

// Data will be cached and updated in background
const { data, isStale } = useQuery({
  queryKey: ['balance'],
  staleTime: 30 * 1000,  // Consider stale after 30 seconds
});

3. Optimistic Updates

Update UI immediately while the mutation is in progress:

const mutation = useMutation({
  mutationFn: updateProfile,
  onMutate: async (newData) => {
    // Optimistically update the cache
    queryClient.setQueryData(['profile'], newData);
  },
});

4. Offline Support

Cached data remains available when offline:

const { data, isLoading, isError } = useQuery({
  queryKey: ['profile'],
  networkMode: 'offlineFirst',  // Use cache when offline
});

Working with BigBlocks Components

The BitcoinQueryProvider is required for these components:

  • MarketTable - Market listings with real-time updates
  • PostButton - Social post creation
  • SocialFeed - Social content feed
  • WalletOverview - Wallet balance and transactions
  • SendBSVButton - Transaction operations

Example with market components:

import { 
  BitcoinQueryProvider,
  MarketTable,
  CreateListingButton 
} from 'bigblocks';

export default function MarketplaceApp() {
  return (
    <BitcoinQueryProvider>
      <BitcoinAuthProvider config={{ apiUrl: '/api' }}>
        <CreateListingButton />
        <MarketTable />
      </BitcoinAuthProvider>
    </BitcoinQueryProvider>
  );
}

Troubleshooting

Missing QueryClient Error

Error: No QueryClient set, use QueryClientProvider to set one

Solution: Ensure BitcoinQueryProvider wraps all components that need React Query.

Stale Closure Issues

// ❌ Bad - stale closure
const queryClient = new QueryClient();

function App() {
  return <BitcoinQueryProvider queryClient={queryClient}>...</BitcoinQueryProvider>;
}

// ✅ Good - fresh instance
function App() {
  const queryClient = new QueryClient();
  return <BitcoinQueryProvider queryClient={queryClient}>...</BitcoinQueryProvider>;
}

Cache Not Clearing

// Clear all queries
queryClient.clear();

// Clear specific queries
queryClient.removeQueries({ queryKey: ['market'] });

// Invalidate to refetch
queryClient.invalidateQueries({ queryKey: ['balance'] });

Best Practices

  1. Provider Order: Always place BitcoinQueryProvider at the root
  2. Query Keys: Use consistent, hierarchical query keys
  3. Stale Times: Set appropriate stale times based on data type
  4. Error Handling: Implement global error handling
  5. Memory Management: Clear cache on logout

API Reference

QueryClient Configuration

interface QueryClientConfig {
  defaultOptions?: {
    queries?: {
      cacheTime?: number;
      staleTime?: number;
      refetchOnWindowFocus?: boolean;
      refetchOnReconnect?: boolean;
      refetchOnMount?: boolean | 'always';
      retry?: boolean | number | ((failureCount: number, error: any) => boolean);
      retryDelay?: number | ((attemptIndex: number) => number);
      networkMode?: 'online' | 'always' | 'offlineFirst';
    };
    mutations?: {
      retry?: boolean | number;
      retryDelay?: number;
      networkMode?: 'online' | 'always' | 'offlineFirst';
    };
  };
}

Notes for Improvement

Simplified Implementation: The actual component is a simple wrapper around React Query's QueryClientProvider. The prompt described many advanced features that would be implemented in the consuming application rather than the provider itself. The actual component focuses on:

  • Providing React Query context
  • Working around bundler limitations
  • Ensuring compatibility with BigBlocks components

The advanced query patterns, real-time updates, and optimization strategies described in the prompt would be implemented by developers using the provider, not within the provider component itself.