Как обрабатывать события в React?

👨‍💻 Frontend Developer 🟠 Может встретиться 🎚️ Средний
#React

Краткий ответ

Обработка событий в React происходит через SyntheticEvent — обёртку над нативными событиями:

  1. onClick — обработка кликов 🖱️
  2. onChange — изменение значений 📝
  3. onSubmit — отправка форм 📤
  4. SyntheticEvent — кроссбраузерная совместимость 🌐
  5. preventDefault() — отмена стандартного поведения ⛔
  6. Делегирование — события привязываются к корню 🎯
function Button() {
  const handleClick = (e) => {
    e.preventDefault();
    console.log('Clicked!');
  };
 
  return <button onClick={handleClick}>Click me</button>;
}

Полный ответ

Обработка событий в React — это мощная система, которая обеспечивает кроссбраузерную совместимость и оптимизацию производительности! React использует SyntheticEvent для унификации событий. 🎭

1. Основы обработки событий

Функциональные компоненты

function EventExample() {
  const handleClick = (event) => {
    console.log('Button clicked!', event);
  };
 
  const handleChange = (event) => {
    console.log('Input changed:', event.target.value);
  };
 
  return (
    <div>
      <button onClick={handleClick}>Click me</button>
      <input onChange={handleChange} placeholder="Type here" />
    </div>
  );
}

Классовые компоненты

class EventExample extends React.Component {
  handleClick = (event) => {
    console.log('Button clicked!', event);
  };
 
  handleChange = (event) => {
    console.log('Input changed:', event.target.value);
  };
 
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>Click me</button>
        <input onChange={this.handleChange} placeholder="Type here" />
      </div>
    );
  }
}

2. SyntheticEvent — обёртка событий

React создаёт SyntheticEvent для кроссбраузерной совместимости:

function SyntheticEventExample() {
  const handleEvent = (syntheticEvent) => {
    console.log('SyntheticEvent:', syntheticEvent);
    console.log('Native event:', syntheticEvent.nativeEvent);
    console.log('Target:', syntheticEvent.target);
    console.log('Type:', syntheticEvent.type);
    
    // Предотвращение стандартного поведения
    syntheticEvent.preventDefault();
    syntheticEvent.stopPropagation();
  };
 
  return (
    <form onSubmit={handleEvent}>
      <input type="text" />
      <button type="submit">Submit</button>
    </form>
  );
}

3. Популярные события

Клики и взаимодействие

function ClickEvents() {
  return (
    <div>
      <button onClick={(e) => console.log('Click')}>Click</button>
      <button onDoubleClick={(e) => console.log('Double click')}>Double</button>
      <div onMouseEnter={(e) => console.log('Mouse enter')}>Hover me</div>
      <div onMouseLeave={(e) => console.log('Mouse leave')}>Leave me</div>
    </div>
  );
}

Формы и ввод

function FormEvents() {
  const [value, setValue] = useState('');
 
  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      console.log('Form submitted:', value);
    }}>
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onFocus={(e) => console.log('Input focused')}
        onBlur={(e) => console.log('Input blurred')}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Клавиатура

function KeyboardEvents() {
  const handleKeyDown = (e) => {
    if (e.key === 'Enter') {
      console.log('Enter pressed');
    }
    if (e.key === 'Escape') {
      console.log('Escape pressed');
    }
  };
 
  return (
    <input
      onKeyDown={handleKeyDown}
      onKeyUp={(e) => console.log('Key up:', e.key)}
      placeholder="Press keys"
    />
  );
}

4. Передача параметров в обработчики

Через стрелочные функции

function ParameterExample() {
  const handleClick = (id, name) => {
    console.log(`Clicked item ${id}: ${name}`);
  };
 
  const items = [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' }
  ];
 
  return (
    <div>
      {items.map(item => (
        <button
          key={item.id}
          onClick={() => handleClick(item.id, item.name)}
        >
          {item.name}
        </button>
      ))}
    </div>
  );
}

Через data-атрибуты

function DataAttributeExample() {
  const handleClick = (e) => {
    const id = e.target.dataset.id;
    const name = e.target.dataset.name;
    console.log(`Clicked item ${id}: ${name}`);
  };
 
  return (
    <div>
      <button data-id="1" data-name="Item 1" onClick={handleClick}>
        Item 1
      </button>
      <button data-id="2" data-name="Item 2" onClick={handleClick}>
        Item 2
      </button>
    </div>
  );
}

5. Практические примеры

Модальное окно

function Modal({ isOpen, onClose, children }) {
  const handleBackdropClick = (e) => {
    if (e.target === e.currentTarget) {
      onClose();
    }
  };
 
  const handleKeyDown = (e) => {
    if (e.key === 'Escape') {
      onClose();
    }
  };
 
  useEffect(() => {
    if (isOpen) {
      document.addEventListener('keydown', handleKeyDown);
      return () => document.removeEventListener('keydown', handleKeyDown);
    }
  }, [isOpen]);
 
  if (!isOpen) return null;
 
  return (
    <div className="modal-backdrop" onClick={handleBackdropClick}>
      <div className="modal-content">
        {children}
        <button onClick={onClose}>Close</button>
      </div>
    </div>
  );
}

Drag and Drop

function DragDropExample() {
  const [draggedItem, setDraggedItem] = useState(null);
 
  const handleDragStart = (e) => {
    setDraggedItem(e.target.textContent);
  };
 
  const handleDragOver = (e) => {
    e.preventDefault(); // Разрешить drop
  };
 
  const handleDrop = (e) => {
    e.preventDefault();
    console.log('Dropped:', draggedItem);
    setDraggedItem(null);
  };
 
  return (
    <div>
      <div
        draggable
        onDragStart={handleDragStart}
        style={{ padding: '10px', border: '1px solid #ccc' }}
      >
        Drag me
      </div>
      <div
        onDragOver={handleDragOver}
        onDrop={handleDrop}
        style={{ padding: '20px', border: '2px dashed #999' }}
      >
        Drop zone
      </div>
    </div>
  );
}

6. Оптимизация производительности

useCallback для стабильных ссылок

function OptimizedList({ items }) {
  const [selectedId, setSelectedId] = useState(null);
 
  // Мемоизируем обработчик
  const handleSelect = useCallback((id) => {
    setSelectedId(id);
  }, []);
 
  return (
    <div>
      {items.map(item => (
        <OptimizedItem
          key={item.id}
          item={item}
          onSelect={handleSelect}
          isSelected={selectedId === item.id}
        />
      ))}
    </div>
  );
}
 
const OptimizedItem = React.memo(({ item, onSelect, isSelected }) => {
  return (
    <div
      onClick={() => onSelect(item.id)}
      style={{ background: isSelected ? '#e0e0e0' : 'white' }}
    >
      {item.name}
    </div>
  );
});

7. Делегирование событий

React автоматически использует делегирование событий:

// React привязывает события к корневому элементу
function EventDelegation() {
  const handleClick = (e) => {
    // e.target - элемент, на который кликнули
    // e.currentTarget - элемент с обработчиком
    console.log('Clicked on:', e.target.tagName);
  };
 
  return (
    <div onClick={handleClick}>
      <p>Click on paragraph</p>
      <button>Click on button</button>
      <span>Click on span</span>
    </div>
  );
}

Лучшие практики

  1. Используйте useCallback для оптимизации 🚀
  2. Избегайте inline функций в render 📝
  3. Проверяйте event.target при делегировании ✅
  4. Используйте preventDefault() когда нужно ⛔
  5. Очищайте слушатели в useEffect 🧹

Частые ошибки

Неправильно:

// Создаёт новую функцию при каждом рендере
<button onClick={() => console.log('Click')}>Click</button>

Правильно:

const handleClick = useCallback(() => {
  console.log('Click');
}, []);
 
<button onClick={handleClick}>Click</button>

Заключение

Обработка событий в React:

  • SyntheticEvent — кроссбраузерная совместимость
  • Делегирование — автоматическая оптимизация
  • preventDefault/stopPropagation — контроль поведения
  • useCallback — оптимизация производительности

Используйте события эффективно для создания интерактивных приложений! 🎯