Overview
The SearchBar component provides a comprehensive filtering interface for cryptocurrencies. It includes three input fields:
- Text search: Search by cryptocurrency name or symbol
- Minimum price: Filter cryptocurrencies above a certain price
- Maximum price: Filter cryptocurrencies below a certain price
The component uses debouncing (300ms delay) to optimize performance by reducing the number of search callbacks triggered while the user is typing.
Props
onSearch
(filter: Filter) => void
required
Callback function invoked when the search filter changes. Called after a 300ms debounce delay.The Filter object contains:
text (string): The search query text
minPrice (number | null): Minimum price filter, or null if not set
maxPrice (number | null): Maximum price filter, or null if not set
Filter Interface
interface Filter {
text: string;
minPrice: number | null;
maxPrice: number | null;
}
Usage Example
Basic Usage
import { useState } from 'react';
import SearchBar from './components/SearchBar';
function CryptoSearch() {
const handleSearch = (filter) => {
console.log('Search text:', filter.text);
console.log('Min price:', filter.minPrice);
console.log('Max price:', filter.maxPrice);
// Apply filtering logic here
};
return <SearchBar onSearch={handleSearch} />;
}
With Filter Context
Typically used with a filter context to share search state across components:
import SearchBar from './components/SearchBar';
import { useFilter } from '../context/FilterContext';
function Header() {
const { setFilter } = useFilter();
return <SearchBar onSearch={setFilter} />;
}
Filtering Cryptocurrency Data
import { useState, useEffect } from 'react';
import SearchBar from './components/SearchBar';
function FilteredCryptoList({ cryptos }) {
const [filtered, setFiltered] = useState(cryptos);
const handleSearch = (filter) => {
const result = cryptos.filter(crypto => {
const matchesText =
crypto.name.toLowerCase().includes(filter.text.toLowerCase()) ||
crypto.symbol.toLowerCase().includes(filter.text.toLowerCase());
const price = parseFloat(crypto.price_usd);
const matchesMinPrice = filter.minPrice ? price >= filter.minPrice : true;
const matchesMaxPrice = filter.maxPrice ? price <= filter.maxPrice : true;
return matchesText && matchesMinPrice && matchesMaxPrice;
});
setFiltered(result);
};
return (
<>
<SearchBar onSearch={handleSearch} />
{/* Render filtered list */}
</>
);
}
Component Behavior
Debouncing
The component implements a 300ms debounce to prevent excessive callback invocations:
useEffect(() => {
const timeout = setTimeout(() => {
onSearch({
text,
minPrice: minPrice ? minPrice : null,
maxPrice: maxPrice ? maxPrice : null,
});
}, 300);
return () => clearTimeout(timeout);
}, [text, minPrice, maxPrice]);
Benefits:
- Reduces API calls or filtering operations
- Improves performance during rapid typing
- Provides smoother user experience
Direct string state:const [text, setText] = useState('');
<TextInput
placeholder="Buscar criptomoneda..."
value={text}
onChangeText={setText}
style={styles.input}
/>
- Accepts any text input
- Case-insensitive search (handled by parent)
- No validation or formatting
Numeric parsing with null handling:const [minPrice, setMinPrice] = useState<number | null>(null);
<TextInput
placeholder="Precio mínimo"
value={minPrice?.toString() ?? ''}
onChangeText={(val) => {
const parsed = parseFloat(val);
setMinPrice(isNaN(parsed) ? null : parsed);
}}
style={styles.input}
keyboardType="numeric"
/>
- Uses numeric keyboard
- Parses input to float
- Sets to
null if invalid (NaN)
- Same logic for both min and max price
Styling Details
Container
container: {
backgroundColor: '#fff',
padding: 12,
borderRadius: 12,
marginBottom: 16,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 6,
elevation: 4,
}
- White background with subtle shadow for elevation
- 12px padding and border radius for rounded appearance
- 16px bottom margin for spacing from content below
- Platform-specific shadows (iOS) and elevation (Android)
input: {
fontSize: 16,
color: '#333',
borderWidth: 1,
marginBottom: 5,
marginTop: 5,
borderColor: '#ddd',
paddingHorizontal: 12,
paddingVertical: 8,
borderRadius: 8,
}
- Light gray border (
#ddd) with 1px width
- 16px font size for readability
- 8px vertical spacing between inputs
- 8px border radius for consistent rounded corners
- Adequate padding for touch targets
Visual Layout
┌─────────────────────────────────────┐
│ ┌───────────────────────────────┐ │
│ │ Buscar criptomoneda... │ │ ← Text search
│ └───────────────────────────────┘ │
│ ┌───────────────────────────────┐ │
│ │ Precio mínimo │ │ ← Min price
│ └───────────────────────────────┘ │
│ ┌───────────────────────────────┐ │
│ │ Precio máximo │ │ ← Max price
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
The 300ms debounce delay is crucial for performance. Without it, every keystroke would trigger the onSearch callback, potentially causing excessive re-renders or API calls.
Cleanup Function
The useEffect hook properly cleans up timeouts to prevent memory leaks:
return () => clearTimeout(timeout);
This ensures that if the user types again before the 300ms delay completes, the previous timeout is cancelled.
- HomeHeader: Header component that integrates SearchBar
- CryptoList: List component that displays filtered results
- CryptoCard: Individual card component for each cryptocurrency
Source Code Location
~/workspace/source/src/components/SearchBar.tsx:30