Sobes.tech
Middle

Task 1 We have an array of 10,000 objects (for example, "Employees" or "Products"). We need to implement a component for selecting an item that won't "freeze" the browser and will be user-friendly. Requirements: 1. Data typing: Create an interface Item with fields id: string, label: string, and category: string. 2. Search (Input): The list should be filtered as text is entered. 3. DOM and event handling (Keyboard Navigation): * The user should be able to navigate the list using Up and Down arrow keys. 4. When Enter is pressed, the selected item is "confirmed". 5. Important nuance: The arrow-selected item should always be in view (programmatically scroll the container if the cursor goes beyond the boundaries). 6. Optimization (Performance): * Since there are many data (10k), discuss or implement simple "lazy rendering" (render only the first 20-30 items or use IntersectionObserver). Component output.

sobes.tech AI

Answer from AI

To implement a component for selecting from a large array (10,000 elements) with search and keyboard navigation, you can do the following:

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]);

  // Keyboard handling
  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: ${selected.label}`);
    }
  }

  // Scroll to highlighted item
  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]);

  // Lazy rendering of first ITEMS_PER_PAGE elements
  const visibleItems = filteredItems.slice(0, ITEMS_PER_PAGE);

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={e => setQuery(e.target.value)}
        onKeyDown={onKeyDown}
        placeholder="Search..."
      />
      <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' }}>
            Showing {ITEMS_PER_PAGE} of {filteredItems.length} items
          </div>
        )}
      </div>
    </div>
  );
}

Key points:

  • Typing with interface Item.
  • Filtering based on user input.
  • Navigation with arrow keys and confirmation with Enter.
  • Scroll to the highlighted item to keep it visible.
  • Lazy rendering — only show the first 30 items to avoid DOM overload.

For more advanced optimization, consider using virtualization (e.g., react-window) or IntersectionObserver for loading items as you scroll.

Task 1 We have an array of 10,000 objects (for… - sobes.tech