Code Example 1: target vs currentTarget
❓ Чем отличается e.target от e.currentTarget? Какие значения будут при клике на кнопку?
function TargetExample() {
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
console.log('target:', e.target);
console.log('currentTarget:', e.currentTarget);
};
return (
<div onClick={handleClick} className="container">
<button>Кнопка</button>
</div>
);
}
Code Example 2: stopPropagation example
❓ Что выведется в консоль при клике на кнопку? Какие обработчики НЕ сработают?
function PropagationExample() {
const div1Handler = () => console.log('div handler 1');
const div2Handler = () => console.log('div handler 2');
const buttonHandler = (e: React.MouseEvent) => {
e.stopPropagation();
console.log('button');
};
return (
<div onClick={div1Handler}>
<div onClick={div2Handler}>
<button onClick={buttonHandler}>Клик</button>
</div>
</div>
);
}
Code Example 3: Modal closing patterns
❓ В чём разница между этими двумя способами? Какой предпочтительнее и почему?
stopPropagation:
function Modal({ onClose, children }) {
return (
<div className="overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
{children}
</div>
</div>
);
}
Target check:
function Modal({ onClose, children }) {
const handleOverlayClick = (e: React.MouseEvent<HTMLDivElement>) => {
if (e.target === e.currentTarget) {
onClose();
}
};
return (
<div className="overlay" onClick={handleOverlayClick}>
<div className="modal-content">
{children}
</div>
</div>
);
}
Code Example 4: Card with action button
❓ Зачем здесь stopPropagation? Что произойдёт при клике на "В корзину" без него?
function ProductCard({ product, onSelect, onAddToCart }) {
const handleCardClick = () => {
onSelect(product.id);
};
const handleAddClick = (e: React.MouseEvent) => {
e.stopPropagation();
onAddToCart(product.id);
};
return (
<div className="card" onClick={handleCardClick}>
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>{product.price} ₽</p>
<button onClick={handleAddClick}>В корзину</button>
</div>
);
}
Code Example 5: Dropdown with click outside
❓ Как работает закрытие dropdown при клике вне его? Зачем проверять contains()?
function Dropdown({ trigger, children }) {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
setIsOpen(false);
}
};
if (isOpen) {
document.addEventListener('click', handleClickOutside);
}
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, [isOpen]);
return (
<div ref={dropdownRef}>
<button onClick={() => setIsOpen(!isOpen)}>{trigger}</button>
{isOpen && <div className="dropdown-menu">{children}</div>}
</div>
);
}
Code Example 6: Event delegation with closest
❓ Как работает closest() в этом примере? Зачем он нужен вместо проверки tagName?
function ItemList({ items }: { items: Item[] }) {
const handleClick = (e: React.MouseEvent<HTMLUListElement>) => {
const target = e.target as HTMLElement;
const li = target.closest('li');
if (!li) return;
const id = li.dataset.id;
const action = target.dataset.action;
switch (action) {
case 'edit':
console.log('Редактировать:', id);
break;
case 'delete':
console.log('Удалить:', id);
break;
default:
console.log('Выбрать:', id);
}
};
return (
<ul onClick={handleClick}>
{items.map(item => (
<li key={item.id} data-id={item.id}>
<span>{item.name}</span>
<button data-action="edit">✏️</button>
<button data-action="delete">🗑️</button>
</li>
))}
</ul>
);
}
Code Example 7: Portal event bubbling
❓ Почему клик по кнопке в портале вызовет "Parent clicked"? Ведь в DOM кнопка находится в другом месте?
function Parent() {
const handleClick = () => console.log('Parent clicked');
return (
<div onClick={handleClick}>
<PortalChild />
</div>
);
}
function PortalChild() {
return ReactDOM.createPortal(
<button>Кнопка в портале</button>,
document.getElementById('portal-root')!
);
}
Code Example 8: stopPropagation anti-pattern
❓ Почему Version A считается антипаттерном? Чем лучше Version B?
Version A:
function BadComponent() {
return (
<div onClick={(e) => { e.stopPropagation(); doSomething(); }}>
<span onClick={(e) => { e.stopPropagation(); doOther(); }}>
<button onClick={(e) => { e.stopPropagation(); doAction(); }}>
Клик
</button>
</span>
</div>
);
}
Version B:
function GoodComponent() {
return (
<div onClick={handleContainer}>
<button onClick={(e) => { e.stopPropagation(); handleAction(); }}>
Действие
</button>
</div>
);
}