Skip to main content
The FilterContext provides global state management for filtering cryptocurrencies by text search and price range across the application.

Filter Interface

The Filter interface defines the structure of the filter state:
src/context/FilterContext.tsx
export interface Filter {
  text: string;
  minPrice: number | null;
  maxPrice: number | null;
}

Properties

text
string
Search text for filtering by cryptocurrency name or symbol. Case-insensitive.
minPrice
number | null
Minimum price filter in USD. null means no minimum price filter.
maxPrice
number | null
Maximum price filter in USD. null means no maximum price filter.

FilterProvider Component

The FilterProvider component wraps your application to provide filter state to all child components.
src/context/FilterContext.tsx
export const FilterProvider = ({ children }: { children: ReactNode }) => {
  const [filter, setFilter] = useState<Filter>({
    text: '',
    minPrice: null,
    maxPrice: null,
  });

  return (
    <FilterContext.Provider value={{ filter, setFilter }}>
      {children}
    </FilterContext.Provider>
  );
};

Setup

Wrap your app’s navigation with FilterProvider:
app/(tabs)/_layout.tsx
import { FilterProvider } from '@/src/context/FilterContext';

export default function TabLayout() {
  return (
    <FilterProvider>
      <Tabs>
        {/* Your tab screens */}
      </Tabs>
    </FilterProvider>
  );
}

useFilter Hook

The useFilter hook provides access to the filter state and setter function.
src/context/FilterContext.tsx
export const useFilter = () => {
  const ctx = useContext(FilterContext);
  
  if (!ctx) throw new Error('useFilter debe usarse dentro de FilterProvider');
  
  return ctx;
};

Return Value

filter
Filter
The current filter state object containing text, minPrice, and maxPrice
setFilter
React.Dispatch<React.SetStateAction<Filter>>
React state setter function for updating the filter. Can accept a new Filter object or a function that receives the previous state.

Error Handling

The useFilter hook must be used within a component tree wrapped by FilterProvider. Using it outside the provider will throw an error.
// ❌ Error: useFilter used outside FilterProvider
const MyComponent = () => {
  const { filter } = useFilter(); // Throws error!
};

// ✅ Correct: Used within FilterProvider
export default function App() {
  return (
    <FilterProvider>
      <MyComponent /> {/* useFilter works here */}
    </FilterProvider>
  );
}

Usage Examples

Basic Filter Usage

import { useFilter } from '@/src/context/FilterContext';

const SearchComponent = () => {
  const { filter, setFilter } = useFilter();
  
  return (
    <TextInput
      value={filter.text}
      onChangeText={(text) => setFilter({ ...filter, text })}
      placeholder="Search cryptocurrencies..."
    />
  );
};

Filtering Cryptocurrencies

Real example from HomeScreen.tsx showing how to use the filter:
src/screens/HomeScreen.tsx
const HomeScreen = () => {
  const { data, loading, error } = useCryptoData();
  const { filter } = useFilter();
  
  // Apply filters to cryptocurrency data
  const filtered = data.filter((crypto) => {
    const nameMatch = crypto.name.toLowerCase().includes(filter.text.toLowerCase());
    const symbolMatch = crypto.symbol.toLowerCase().includes(filter.text.toLowerCase());
    
    const minOk = filter.minPrice === null || Number(crypto.price_usd) >= filter.minPrice;
    const maxOk = filter.maxPrice === null || Number(crypto.price_usd) <= filter.maxPrice;
    
    return (nameMatch || symbolMatch) && minOk && maxOk;
  });
  
  return (
    <ScrollView>
      {filtered.map((crypto) => (
        <CryptoCard key={crypto.id} crypto={crypto} />
      ))}
    </ScrollView>
  );
};

Updating Multiple Filter Properties

const PriceRangeComponent = () => {
  const { filter, setFilter } = useFilter();
  
  const handleMinPriceChange = (value: string) => {
    const price = value ? parseFloat(value) : null;
    setFilter({ ...filter, minPrice: price });
  };
  
  const handleMaxPriceChange = (value: string) => {
    const price = value ? parseFloat(value) : null;
    setFilter({ ...filter, maxPrice: price });
  };
  
  return (
    <View>
      <TextInput
        value={filter.minPrice?.toString() || ''}
        onChangeText={handleMinPriceChange}
        placeholder="Min Price (USD)"
        keyboardType="numeric"
      />
      <TextInput
        value={filter.maxPrice?.toString() || ''}
        onChangeText={handleMaxPriceChange}
        placeholder="Max Price (USD)"
        keyboardType="numeric"
      />
    </View>
  );
};

Clearing Filters

const ClearFiltersButton = () => {
  const { setFilter } = useFilter();
  
  const handleClear = () => {
    setFilter({
      text: '',
      minPrice: null,
      maxPrice: null,
    });
  };
  
  return (
    <TouchableOpacity onPress={handleClear}>
      <Text>Clear Filters</Text>
    </TouchableOpacity>
  );
};

Filter Logic Breakdown

The filtering logic follows this pattern:
1

Text matching

Check if the search text appears in either the cryptocurrency name or symbol (case-insensitive)
2

Minimum price check

If minPrice is set, ensure the crypto’s price is greater than or equal to it
3

Maximum price check

If maxPrice is set, ensure the crypto’s price is less than or equal to it
4

Combine conditions

A cryptocurrency passes the filter if it matches the text search AND falls within the price range

Default State

The filter initializes with these default values:
{
  text: '',        // No search text
  minPrice: null,  // No minimum price filter
  maxPrice: null   // No maximum price filter
}
With default values, all cryptocurrencies pass the filter.

Best Practices

When updating based on previous state, use the functional form:
setFilter(prev => ({ ...prev, text: 'Bitcoin' }));
Add debouncing to text search to avoid excessive re-renders:
const [searchText, setSearchText] = useState('');

useEffect(() => {
  const timer = setTimeout(() => {
    setFilter(prev => ({ ...prev, text: searchText }));
  }, 300);
  
  return () => clearTimeout(timer);
}, [searchText]);
Always validate and handle invalid price inputs:
const price = value ? parseFloat(value) : null;
if (price !== null && (isNaN(price) || price < 0)) {
  return; // Invalid input
}
setFilter({ ...filter, minPrice: price });

useFilter Hook

Detailed hook documentation

Filtering Feature

Complete filtering guide

State Management

Application state architecture

SearchBar Component

Search UI component

Build docs developers (and LLMs) love