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
Search text for filtering by cryptocurrency name or symbol. Case-insensitive.
Minimum price filter in USD. null means no minimum price filter.
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:
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
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:
Text matching
Check if the search text appears in either the cryptocurrency name or symbol (case-insensitive)
Minimum price check
If minPrice is set, ensure the crypto’s price is greater than or equal to it
Maximum price check
If maxPrice is set, ensure the crypto’s price is less than or equal to it
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
Use functional updates for complex state changes
When updating based on previous state, use the functional form:setFilter(prev => ({ ...prev, text: 'Bitcoin' }));
Debounce text input for performance
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]);
useFilter Hook
Detailed hook documentation
Filtering Feature
Complete filtering guide
State Management
Application state architecture
SearchBar Component
Search UI component