Middle
Ασκήσεις 1 Έχουμε έναν πίνακα 10.000 αντικειμένων (π.χ., "Υπάλληλοι" ή "Προϊόντα"). Πρέπει να υλοποιήσουμε ένα συστατικό επιλογής στοιχείου που δεν "παγώνει" το πρόγραμμα περιήγησης και θα είναι βολικό για τον χρήστη. Τι πρέπει να υλοποιηθεί (Απαιτήσεις): 1. Τυποποίηση δεδομένων: Δημιουργήστε μια διεπαφή Item με πεδία id: string, label: string και category: string. 2. Αναζήτηση (Input): Κατά την εισαγωγή κειμένου, η λίστα πρέπει να φιλτράρεται. 3. Εργασία με DOM και γεγονότα (Πλοήγηση με πληκτρολόγιο): * Ο χρήστης πρέπει να μπορεί να κινείται στη λίστα με τα πλήκτρα Up και Down. 4. Πατώντας Enter, το επιλεγμένο στοιχείο "επιβεβαιώνεται". 5. Σημείωση σημαντική: Το στοιχείο που επιλέγεται με τα πλήκτρα πρέπει πάντα να είναι ορατό (προγραμματίστε την κύλιση του κοντέινερ αν ο δείκτης βγαίνει από τα όρια). 6. Βελτιστοποίηση (Απόδοση): * Δεδομένου ότι υπάρχουν πολλά δεδομένα (10k), συζητήστε ή υλοποιήστε μια απλή "τεμπέλικη απόδοση" (απλώς αποδώστε τα πρώτα 20-30 στοιχεία ή χρησιμοποιήστε IntersectionObserver). Έξοδος του συστατικού
sobes.tech AI
Απάντηση από AI
Для реализации компонента выбора из большого массива (10 000 элементов) с поиском и навигацией клавиатурой можно сделать так:
interface Item {
id: string;
label: string;
category: string;
}
const ITEMS_PER_PAGE = 30;
function ItemSelector({ items }: { items: Item[] }) {
const [query, setQuery] = React.useState('');
const [filteredItems, setFilteredItems] = React.useState<Item[]>(items);
const [highlightedIndex, setHighlightedIndex] = React.useState(0);
const containerRef = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
const filtered = items.filter(item =>
item.label.toLowerCase().includes(query.toLowerCase())
);
setFilteredItems(filtered);
setHighlightedIndex(0);
}, [query, items]);
// Обработка клавиш
function onKeyDown(e: React.KeyboardEvent) {
if (e.key === 'ArrowDown') {
e.preventDefault();
setHighlightedIndex(i => Math.min(i + 1, filteredItems.length - 1));
} else if (e.key === 'ArrowUp') {
e.preventDefault();
setHighlightedIndex(i => Math.max(i - 1, 0));
} else if (e.key === 'Enter') {
e.preventDefault();
const selected = filteredItems[highlightedIndex];
if (selected) alert(`Выбран: ${selected.label}`);
}
}
// Скролл к выделенному элементу
React.useEffect(() => {
const container = containerRef.current;
if (!container) return;
const itemElements = container.querySelectorAll('.item');
const current = itemElements[highlightedIndex] as HTMLElement | undefined;
if (current) {
const containerTop = container.scrollTop;
const containerBottom = containerTop + container.clientHeight;
const elemTop = current.offsetTop;
const elemBottom = elemTop + current.offsetHeight;
if (elemTop < containerTop) {
container.scrollTop = elemTop;
} else if (elemBottom > containerBottom) {
container.scrollTop = elemBottom - container.clientHeight;
}
}
}, [highlightedIndex]);
// Ленивый рендеринг первых ITEMS_PER_PAGE элементов
const visibleItems = filteredItems.slice(0, ITEMS_PER_PAGE);
return (
<div>
<input
type="text"
value={query}
onChange={e => setQuery(e.target.value)}
onKeyDown={onKeyDown}
placeholder="Поиск..."
/>
<div
ref={containerRef}
style={{ maxHeight: 300, overflowY: 'auto', border: '1px solid #ccc' }}
tabIndex={0}
onKeyDown={onKeyDown}
>
{visibleItems.map((item, index) => (
<div
key={item.id}
className="item"
style={{
padding: '4px 8px',
backgroundColor: index === highlightedIndex ? '#bde4ff' : 'transparent',
}}
>
{item.label} ({item.category})
</div>
))}
{filteredItems.length > ITEMS_PER_PAGE && (
<div style={{ padding: 8, fontStyle: 'italic' }}>
Показано {ITEMS_PER_PAGE} из {filteredItems.length} элементов
</div>
)}
</div>
</div>
);
}
Ключевые моменты:
- Типизация через интерфейс
Item. - Фильтрация по вводу пользователя.
- Навигация стрелками и подтверждение Enter.
- Скролл к выделенному элементу, чтобы он всегда был виден.
- Ленивый рендеринг — показываем только первые 30 элементов, чтобы не перегружать DOM.
Для более продвинутой оптимизации можно использовать виртуализацию (например, react-window) или IntersectionObserver для подгрузки элементов по мере прокрутки.