Olaylara Tepki Verme
React, JSX’inize olay yöneticileri eklemenize olanak tanır. Olay yöneticileri; tıklamak, fareyle üzerine gelmek ve form girdilerine odaklanmak gibi kullanıcı aksiyonlarına tepki verirken tetiklenecek olan fonksiyonlarınızdır.
Bunları öğreneceksiniz
- Olay yöneticisi yazmanın farklı biçimleri
- Üst bileşenden (parent component) olay yönetimi mantığının nasıl iletileceği
- Olayların nasıl yayıldığı (propagation) ve nasıl durdurulacağı
Olay yöneticileri oluşturmak
Olay yöneticisi (event handler) oluşturmak için bir fonksiyon tanımlayıp uygun JSX etiketine prop olarak iletirsiniz. Örneğin, hiçbir şey yapmayan bir butonu ele alalım:
export default function Button() { return ( <button> Hiçbir şey yapmıyorum </button> ); }
Kullanıcı düğmeye tıkladığında bir mesaj göstermek için aşağıdaki adımları izleyebilirsiniz:
Buttonbileşeninizin içerisindehandleClickisimli bir fonksiyon tanımlayın.- Tanımladığınız fonksiyonun içerisinde mantığınızı oluşturun (mesaj göstermek için
alertkullanın). <button>JSX etiketineonClick={handleClick}ekleyin.
export default function Button() { function handleClick() { alert('Bana tıkladın!'); } return ( <button onClick={handleClick}> Bana tıkla </button> ); }
handleClick fonksiyonunu tanımladınız ve ardından <button>’a prop olarak ilettiniz. handleClick, bir olay yöneticisi (event handler)‘dir. Olay yönetici fonksiyonları:
- Genellikle bileşenlerinizin içerisinde tanımlanır.
handleile başlayıp olayın ismiyle devam edecek formatta isimlendirilirler.
Geleneksel olarak olay yöneticilerinin isimlerinin handle ile başlaması yaygındır. İncelediğiniz projelerde onClick={handleClick} ve onMouseEnter={handleMouseEnter} gibi kulanımları sıkça görürsünüz.
Alternatif olarak, olay yöneticilerini JSX içinde tanımlayabilirsiniz:
<button onClick={function handleClick() {
alert('Bana tıkladın!');
}}>Ya da ok fonksiyonlarını kullanarak kısaltabilirsiniz:
<button onClick={() => {
alert('Bana tıkladın!');
}}>Bu yazım biçimleri eşdeğerdir. Satır içi olay yöneticileri, basit fonksiyonlar için kullanışlıdır.
Olay yöneticilerinde prop’ları okumak
Olay yöneticileri, bileşenlerin içerisinde tanımlandığından prop’lara erişebilirler. Tıklandığında message prop’unun değerini içeren bir uyarı gösteren bir buton örneğine bakalım:
function AlertButton({ message, children }) { return ( <button onClick={() => alert(message)}> {children} </button> ); } export default function Toolbar() { return ( <div> <AlertButton message="Oynatılıyor!"> Film Oynat </AlertButton> <AlertButton message="Yükleniyor!"> Resim Yükle </AlertButton> </div> ); }
Bu iki düğmenin farklı mesaj gösterebilmesine olanak sağlar. Bileşenlere ilettiğiniz mesajları değiştirmeyi deneyin.
Olay yöneticilerini prop olarak iletmek
Sıklıkla bileşenlerin alt bileşenlerindeki (child component) olay yöneticilerini belirlemesini istersiniz. Düğmeleri düşünelim: bileşeninin nerede kullanıldığına bağlı olarak farklı işlevler yerine getirmesini isteyebilirsiniz - mesela biri film oynatırken diğeri resim yükleyebilir.
Bunun için, üst bileşenden (parent component) prop olarak alınan fonksiyon olay yöneticisi olarak kullanılabilir:
function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); } function PlayButton({ movieName }) { function handlePlayClick() { alert(`${movieName} oynatılıyor!`); } return ( <Button onClick={handlePlayClick}> "{movieName}" Filmini Oynat </Button> ); } function UploadButton() { return ( <Button onClick={() => alert('Yükleniyor!')}> Resim Yükle </Button> ); } export default function Toolbar() { return ( <div> <PlayButton movieName="Hababam Sınıfı" /> <UploadButton /> </div> ); }
Toolbar bileşeni, PlayButton ve UploadButton bileşenlerini render eder:
PlayButton, içerisindekiButtonbileşenineonClickprop’u içinhandlePlayClickfonksiyonunu iletir.UploadButton, içerisindekiButtonbileşenineonClickprop’u için() => alert('Yükleniyor!')fonksiyonunu iletir.
Son olarak, Button bileşeniniz onClick prop’unu kabul eder ve doğrudan onClick={onClick} şeklinde yerleşik <button> elementine aktarır. Böylece React, düğmeye tıkladıkça ilettiğiniz fonksiyonu çağırır.
Bu durum tasarım sistemlerinde (design system) yaygınca kullanılır. Tasarım sistemi bileşenleri varsayılan stillendirmelere sahiptirler ancak davranış tanımlamazlar. Böylece PlayButton ve UploadButton bileşenlerinde olduğu gibi, olay yöneticileri iletilerek davranış belirlenir.
Olay yönetici prop’larını adlandırmak
<button> ve <div> gibi yerleşik bileşenler, yalnızca onClick gibi tarayıcı olay adlarını prop adı olarak destekler. Ancak kendi bileşenlerinizin olay yönetici prop’larını istediğiniz gibi adlandırabilirsiniz.
Geleneksel olarak, olay yönetici prop’ları on ile başlamalı ve büyük harfle devam etmelidir.
Örneğin, Button bileşeninin onClick prop’u onSmash olarak da adlandırılabilirdi:
function Button({ onSmash, children }) { return ( <button onClick={onSmash}> {children} </button> ); } export default function App() { return ( <div> <Button onSmash={() => alert('Oynatılıyor!')}> Film Oynat </Button> <Button onSmash={() => alert('Yükleniyor!')}> Resim Yükle </Button> </div> ); }
Bu örnekte, <button onClick={onSmash}> ifadesindeki <button> (küçük harfle) tıklama olayı için onClick isminde bir prop almak zorundadır ancak özel Button bileşeninizin prop adı tamamen size kalmıştır!
Bileşeniniz birden fazla etkileşimi desteklediğinde, olay işleyicisi prop’larını uygulamaya özgü konseptler için adlandırabilirsiniz. Örneğin, Toolbar bileşeni onPlayMovie ve onUploadImage olay yöneticilerini alır:
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Oynatılıyor!')} onUploadImage={() => alert('Yükleniyor!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Film Oynat </Button> <Button onClick={onUploadImage}> Resim Yükle </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
Dikkat ederseniz App bileşeni, Toolbar bileşeninin onPlayMovie veya onUploadImage ile ne yapacağını bilmek zorunda değildir. Bu Toolbar bileşeninin implementasyon detayıdır. Toolbar, bu prop’ları Button’larına onClick olay yöneticisi olarak iletir. İleriki zamanlarda tıklama yerine klavye kısayoluyla da tetikletmek isteyebilirsiniz. Bileşenlerinizin prop’larını onPlayMovie gibi uygulamaya özgü etkileşimlere göre adlandırmak ileride kullanım biçimlerini değiştirme esnekliği sağlar.
Olayların yayılması
Olay yöneticileri aynı zamanda alt elementlerden gelen olayları da yakalar. Bu durum, olayların “kabarması” (bubble) ya da “yayılması” (propagate) olarak adlandırılır. Meydana geldiği yerde başlar ve DOM ağacında yukarı doğru ilerler.
Örnekteki <div> iki düğme içerir. Hem <div> hem de düğmeler kendi onClick yöneticilerine sahiptirler. Bir düğmeye tıkladığınızda hangi yöneticilerin tetikleneceğini düşünürsünüz?
export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('Araç çubuğuna tıkladın!'); }}> <button onClick={() => alert('Oynatılıyor!')}> Film Oynat </button> <button onClick={() => alert('Yükleniyor!')}> Resim Yükle </button> </div> ); }
Bir düğmeye tıkladığınızda, önce kendi onClick’i çalışır sonrasında üst element olan div’in onClick’i çalışır. Bu sebeple iki mesaj belirir. Eğer araç çubuğuna tıklarsanız, yalnızca div’in onClick’i çalışır.
Yayılımı durdurmak
Olay yöneticileri argüman olarak yalnızca bir olay nesnesi (event object) alır. Genellikle olay teriminin İngilizce karşılığı “event”in kısaltılması olan e ile adlandırılırlar. Bu nesneyi kullanarak olay hakkındaki detaylara erişebilirsiniz.
Olay nesnesi ayrıca yayılımı durdurmanıza da imkan sağlar. Bir olayın üst bileşenlere erişmesini engellemek istiyorsanız, örnekteki Button bileşeninde olduğu gibi e.stopPropagation() çağırmalısınız.
function Button({ onClick, children }) { return ( <button onClick={e => { e.stopPropagation(); onClick(); }}> {children} </button> ); } export default function Toolbar() { return ( <div className="Toolbar" onClick={() => { alert('Araç çubuğuna tıkladın!'); }}> <Button onClick={() => alert('Oynatılıyor!')}> Film Oynat </Button> <Button onClick={() => alert('Yükleniyor!')}> Resim Yükle </Button> </div> ); }
Düğmelerden birisine tıkladığınızda:
- React,
<button>’a iletilenonClickyöneticisini çağırır. - Bu yönetici,
Buttoniçerisinde tanımlanır ve şunları yapar:- Olayın daha fazla kabarmasını (bubbling) önlemek için
e.stopPropagation()metodunu çağırır. Toolbarbileşeninden prop olarak iletilenonClickfonksiyonunu çağırır.
- Olayın daha fazla kabarmasını (bubbling) önlemek için
- Bu fonksiyon
Toolbarbileşeninde tanımlanır ve düğmenin kendi uyarısını görüntüler. - Yayılım durdurulduğu için üstteki
<div>elementininonClickyöneticisi çalışmaz.
Düğmeye tıklandığında <button> ve <div> elementlerinden gelen iki ayrı uyarı gösterilirken e.stopPropagation() kullanıldığında yalnızca <button> elementinden gelen uyarı gösterilir. Düğmeye tıklamak, fonksiyonellik açısından araç çubuğuna tıklamakla aynı şey değildir. Dolayısıyla da olayın yayılımının durdurulması arayüz açısından oldukça mantıklıdır.
Derinlemesine İnceleme
Nadir durumlarda yayılması durdurulmuş olsa bile alt elemanlardaki olayları yakalamak isteyebilirsiniz. Örneğin, analitik verileri için her tıklamayı kaydetmek istebilirsiniz. Bu yayılım mantığından bağımsızdır. Bunu olay adının sonuna Capture ekleyerek yapabilirsiniz:
<div onClickCapture={() => { /* ilk bu çalışır */ }}>
<button onClick={e => e.stopPropagation()} />
<button onClick={e => e.stopPropagation()} />
</div>Her olay üç aşamada yayılır:
- Aşağıya doğru ilerleyecek şekilde tüm
onClickCaptureyöneticilerini çalıştırır. - Tıklanan elementin
onClickyöneticisi çalıştırır. - Yukarı doğru ilerleyecek şekilde tüm
onClickyöneticilerini çalıştırır.
Olayları yakalamak, yönlendirici (router) ya da analitik kodları için faydalıdır ancak muhtemelen uygulamanızda kullanmayacaksınız.
Yayılıma alternatif olarak yöneticileri iletmek
Örnekteki tıklama yöneticisinin önce bir kod satırını yürüttüğüne, ardından üst bileşenden iletilen onClick prop’unu çağırdığına dikkat edin:
function Button({ onClick, children }) {
return (
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
{children}
</button>
);
}Bu yöneticiye, üst bileşenin onClick olay yöneticisini çağırmadan önce, istediğiniz kadar kod ekleyebilirsiniz. Bu kalıp, yayılıma alternatif sağlar. Alt bileşenin olayı yönetmesine izin verirken, üst bileşenin ek davranışları belirlemesine olanak tanır. Yayılımın aksine otomatik değildir. Ancak bu kalıbın faydası, bir olay sonucunda yürütülen kod zincirinin tamamını net bir şekilde takip edebilmenizdir.
Eğer yayılıma güvenmiyorsanız ve hangi yöneticlerin neden çalıştığını takip etmekte zorlanıyorsanız bu yaklaşımı deneyin.
Varsayılan davranışı önlemek
Bazı tarayıcı olayları, kendisine has varsayılan davranışlara sahiptir. Örneğin, <form>’un gönderme (submit) olayı, içerisindeki düğmeye tıklandığında varsayılan olarak tüm sayfanın yeniden yüklenmesine neden olur:
export default function Signup() { return ( <form onSubmit={() => alert('Gönderiliyor!')}> <input /> <button>Gönder</button> </form> ); }
Bu durumu önlemek için olay nesnesinden e.preventDefault() metodunu çağırabilirsiniz:
export default function Signup() { return ( <form onSubmit={e => { e.preventDefault(); alert('Gönderiliyor!'); }}> <input /> <button>Gönder</button> </form> ); }
e.stopPropagation() ve e.preventDefault() metodlarını birbiriyle karıştırmayın. İkisi de yararlıdır ancak birbiriyle ilişkileri yoktur:
e.stopPropagation(), üstündeki etiketlere eklenen olay yöneticilerinin tetiklenmesini önler.e.preventDefault(), bazı olayların sahip olduğu varsayılan tarayıcı davranışını önler.
Olay yöneticilerinin yan etkileri olabilir mi?
Kesinlikle! Olay yöneticileri yan etkiler (side effect) için en iyi yerdir.
Renderlama fonksiyonlarının aksine, olay yöneticilerinin saf (pure) olması gerekmez. Bu sebeple bir şeyleri değiştirmek için güzel yerlerdir - örneğin, yazma eylemine tepki olarak girdi değerini değiştirmek veya düğmeye basma eylemine tepki olarak listeyi değiştirmek. Ancak, bilgiyi değiştirmek için öncelikle bir yerde saklamak gerekir. React’da bunu yapmak için state, bileşen hafızası kullanabilirsiniz. Bununla ilgili tüm detayları bir sonraki sayfada öğreneceksiniz.
Özet
<button>gibi elementlere fonksiyonları prop olarak ileterek olayları yönetebilirsiniz.- Olay yöneticileri iletilmelidir, çağırılmamalıdır!
onClick={handleClick}doğru kullanımdır,onClick={handleClick()}ise yanlış kullanımdır. - Olay yönetici fonksiyonunu ayrı yada satır içi tanımlayabilirsiniz.
- Olay yöneticileri bileşenin içerisinde tanımlanır, bu nedenle prop’lara erişebilirler.
- Olay yöneticilerini üst bileşende tanımlayıp alt bileşene prop olarak iletebilirsiniz.
- Uygulamaya özgü isimleri kullanarak kendi olay yönetici prop’larınızı tanımlayabilirsiniz.
- Olaylar yukarı doğru yayılır. Önlemek için
e.stopPropagation()’ı yönetici fonksiyonun en üstünde çağırın. - Olaylar, istenmeyen varsayılan tarayıcı davranışına sahip olabilir. Önlemek için
e.preventDefault()’u çağırın. - Olay yöneticisi prop’unu alt bileşenin yöneticisinde açıkca çağırmak, yayılıma iyi bir alternatiftir.
Problem 1 / 2: Olay yöneticisini düzeltin
Örnekteki düğmeye tıklandığında sayfa arka planının beyaz ve siyah arasında geçiş yapması beklenir ancak tıkladığınızda hiçbir şey olmaz. Bu problemi çözün. (handleClick içerisindeki mantıkla ilgilenmeyin—o kısım sorunsuzdur.)
export default function LightSwitch() { function handleClick() { let bodyStyle = document.body.style; if (bodyStyle.backgroundColor === 'black') { bodyStyle.backgroundColor = 'white'; } else { bodyStyle.backgroundColor = 'black'; } } return ( <button onClick={handleClick()}> Işıkları aç/kapat </button> ); }