İçeriğe geç
react

React'te Event Handling: Olay Yönetimi Rehberi

Tarık Tunç
React'te Event Handling: Olay Yönetimi Rehberi

React'te Event Handling: Olay Yönetimi Rehberi

Event handling (olay yönetimi), kullanıcı etkileşimlerini yakalamak ve yanıt vermek için her web uygulamasının temel taşıdır. React'te event handling, native DOM event'lerinden farklı bir mekanizma kullanır: Synthetic Events (Sentetik Olaylar). Bu rehberde React'te event handling'in nasıl çalıştığını, yaygın olay türlerini ve ileri seviye teknikleri detaylı olarak ele alacağız.

Synthetic Events (Sentetik Olaylar)

React, tarayıcılar arası tutarlılık sağlamak için native DOM event'lerini sarmalayan bir SyntheticEvent sistemi kullanır. Bu sistem, tüm tarayıcılarda aynı API'yi sunar:

function EventDemo() {
  const handleClick = (event) => {
    // event bir SyntheticEvent'tir
    console.log('Event tipi:', event.type);           // 'click'
    console.log('Hedef eleman:', event.target);        // tıklanan element
    console.log('Geçerli hedef:', event.currentTarget); // handler'ın bağlı olduğu element
    console.log('Zaman damgası:', event.timeStamp);

    // Native event'e erişim (nadiren gerekli)
    console.log('Native event:', event.nativeEvent);
  };

  return <button onClick={handleClick}>Tıkla</button>;
}
React event handler'ları HTML attribute'larından farklı olarak camelCase yazılır: onclick yerine onClick, onchange yerine onChange. Handler olarak string değil, fonksiyon referansı verilir.

Yaygın Event Türleri

Mouse Events

function MouseEvents() {
  return (
    <div
      onClick={(e) => console.log('Tıklandı', e.clientX, e.clientY)}
      onDoubleClick={() => console.log('Çift tıklandı')}
      onMouseEnter={() => console.log('Mouse girdi')}
      onMouseLeave={() => console.log('Mouse çıktı')}
      onContextMenu={(e) => {
        e.preventDefault(); // Sağ tık menüsünü engelle
        console.log('Sağ tıklama');
      }}
      style={{ padding: '40px', backgroundColor: '#f0f0f0' }}
    >
      Mouse event alanı
    </div>
  );
}

Form Events

function FormEvents() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    bio: '',
    role: 'user'
  });

  // Tüm input'lar için tek handler
  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: type === 'checkbox' ? checked : value
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault(); // Sayfa yenilenmesini engelle
    console.log('Form verileri:', formData);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="username"
        value={formData.username}
        onChange={handleChange}
        onFocus={() => console.log('Input odaklandı')}
        onBlur={() => console.log('Input odağı kaybetti')}
        placeholder="Kullanıcı adı"
      />

      <input
        name="email"
        type="email"
        value={formData.email}
        onChange={handleChange}
        placeholder="E-posta"
      />

      <textarea
        name="bio"
        value={formData.bio}
        onChange={handleChange}
        placeholder="Biyografi"
      />

      <select name="role" value={formData.role} onChange={handleChange}>
        <option value="user">Kullanıcı</option>
        <option value="admin">Yönetici</option>
        <option value="editor">Editör</option>
      </select>

      <button type="submit">Kaydet</button>
    </form>
  );
}

Keyboard Events

function KeyboardShortcuts() {
  const [output, setOutput] = useState('');

  const handleKeyDown = (e) => {
    // Ctrl+Enter ile gönder
    if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
      e.preventDefault();
      console.log('Ctrl+Enter: Gönder');
      return;
    }

    // Escape ile temizle
    if (e.key === 'Escape') {
      setOutput('');
      return;
    }

    // Ctrl+B ile kalın yazı toggle
    if (e.key === 'b' && (e.ctrlKey || e.metaKey)) {
      e.preventDefault();
      console.log('Ctrl+B: Kalın yazı');
    }
  };

  return (
    <div>
      <textarea
        value={output}
        onChange={(e) => setOutput(e.target.value)}
        onKeyDown={handleKeyDown}
        placeholder="Ctrl+Enter: Gönder | Esc: Temizle"
        rows={5}
      />
      <p>Karakter sayısı: {output.length}</p>
    </div>
  );
}

İpucu: onKeyDown ve onKeyUp arasında önemli bir fark vardır. onKeyDown tuşa basıldığında (karakter ekranda görünmeden önce), onKeyUp tuş bırakıldığında tetiklenir. Form validasyonu ve kısayollar için genellikle onKeyDown tercih edilir.

Event Delegation ve Propagation

function EventPropagation() {
  const handleParentClick = () => {
    console.log('Parent tıklandı');
  };

  const handleChildClick = (e) => {
    // Olayın üst elemanlara yayılmasını durdur
    e.stopPropagation();
    console.log('Child tıklandı');
  };

  return (
    <div onClick={handleParentClick} style={{ padding: '20px', backgroundColor: '#eee' }}>
      <p>Parent alan - tıklayın</p>
      <button onClick={handleChildClick}>
        Child buton (stopPropagation)
      </button>
    </div>
  );
}

// Event delegation pattern - liste için tek handler
function TaskList({ tasks, onToggle, onDelete }) {
  const handleClick = (e) => {
    const { action, taskId } = e.target.dataset;
    if (!action) return;

    const id = Number(taskId);
    if (action === 'toggle') onToggle(id);
    if (action === 'delete') onDelete(id);
  };

  return (
    <ul onClick={handleClick}>
      {tasks.map(task => (
        <li key={task.id}>
          <span data-action="toggle" data-task-id={task.id}>
            {task.done ? '✓' : '○'} {task.text}
          </span>
          <button data-action="delete" data-task-id={task.id}>Sil</button>
        </li>
      ))}
    </ul>
  );
}

Drag and Drop Events

function DragDropList() {
  const [items, setItems] = useState(['React', 'Vue', 'Angular', 'Svelte']);
  const [draggedIndex, setDraggedIndex] = useState(null);

  const handleDragStart = (e, index) => {
    setDraggedIndex(index);
    e.dataTransfer.effectAllowed = 'move';
  };

  const handleDragOver = (e, index) => {
    e.preventDefault();
    if (draggedIndex === index) return;

    setItems(prev => {
      const updated = [...prev];
      const [removed] = updated.splice(draggedIndex, 1);
      updated.splice(index, 0, removed);
      return updated;
    });
    setDraggedIndex(index);
  };

  const handleDragEnd = () => {
    setDraggedIndex(null);
  };

  return (
    <ul className="drag-list">
      {items.map((item, index) => (
        <li
          key={item}
          draggable
          onDragStart={(e) => handleDragStart(e, index)}
          onDragOver={(e) => handleDragOver(e, index)}
          onDragEnd={handleDragEnd}
          style={{
            opacity: draggedIndex === index ? 0.5 : 1,
            cursor: 'grab',
            padding: '8px',
            margin: '4px 0',
            backgroundColor: '#f5f5f5',
            borderRadius: '4px'
          }}
        >
          {item}
        </li>
      ))}
    </ul>
  );
}

Uyarı: Event handler'larda doğrudan setState yerine inline arrow function kullanımı (onClick={() => handleClick(id)}) her render'da yeni bir fonksiyon oluşturur. Bu, React.memo ile sarılmış bileşenlerde gereksiz re-render'lara yol açabilir. Performans kritik durumlarda useCallback kullanın.

Sonuç

React'te event handling, Synthetic Events sistemi sayesinde tarayıcılar arası tutarlı ve güvenli bir deneyim sunar. Form yönetimi, klavye kısayolları, drag-and-drop ve event delegation gibi pattern'leri doğru kullanarak etkileşimli ve performanslı uygulamalar oluşturabilirsiniz. preventDefault ve stopPropagation'ı bilinçli kullanın, gereksiz re-render'ları önlemek için handler fonksiyonlarınızı optimize edin.

Kaynaklar

İlgili Yazılar