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:
Button
bileşeninizin içerisindehandleClick
isimli bir fonksiyon tanımlayın.- Tanımladığınız fonksiyonun içerisinde mantığınızı oluşturun (mesaj göstermek için
alert
kullanı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.
handle
ile 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çerisindekiButton
bileşenineonClick
prop’u içinhandlePlayClick
fonksiyonunu iletir.UploadButton
, içerisindekiButton
bileşenineonClick
prop’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 iletilenonClick
yöneticisini çağırır. - Bu yönetici,
Button
içerisinde tanımlanır ve şunları yapar:- Olayın daha fazla kabarmasını (bubbling) önlemek için
e.stopPropagation()
metodunu çağırır. Toolbar
bileşeninden prop olarak iletilenonClick
fonksiyonunu çağırır.
- Olayın daha fazla kabarmasını (bubbling) önlemek için
- Bu fonksiyon
Toolbar
bileşeninde tanımlanır ve düğmenin kendi uyarısını görüntüler. - Yayılım durdurulduğu için üstteki
<div>
elementininonClick
yö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
onClickCapture
yöneticilerini çalıştırır. - Tıklanan elementin
onClick
yöneticisi çalıştırır. - Yukarı doğru ilerleyecek şekilde tüm
onClick
yö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> ); }