Olayları Efektlerinden Ayırma
Olay yöneticileri yalnızca aynı etkileşimi tekrar gerçekleştirdiğinizde yeniden çalışır. Olay yöneticileri aksine, Efektler bir prop veya state değişkeni gibi okudukları bir değerin son render sırasında olduğundan farklı olması durumunda yeniden senkronize olur. Bazen, her iki davranışın bir karışımını da istersiniz: bazı değerlere yanıt olarak yeniden çalışan ancak diğerlerine yanıt vermeyen bir Efekt. Bu sayfa size bunu nasıl yapacağınızı öğretecek.
Bunları öğreneceksiniz
- Bir olay yöneticisi ile bir Efekt arasında nasıl seçim yapılır?
- Efektler neden reaktiftir ve olay yöneticileri değildir?
- Efektinizin kodunun bir bölümünün reaktif olmamasını istediğinizde ne yapmalısınız?
- Efekt olaylarının ne olduğu ve Efektlerinizden nasıl çıkarılacağı
- Efekt olaylarını kullanarak Efektlerden en son sahne ve durum nasıl okunur?
Olay yöneticileri ve Efektler arasında seçim yapma
İlk olarak, olay yöneticileri ve Efektler arasındaki farkı özetleyelim.
Bir sohbet odası bileşeni oluşturduğunuzu düşünün. Gereksinimleriniz şuna benziyor:
- Bileşeniniz seçilen sohbet odasına otomatik olarak bağlanmalıdır.
- ”Gönder” düğmesine tıkladığınızda, sohbete bir mesaj göndermelidir.
Diyelim ki bunlar için kodu zaten uyguladınız, ancak nereye koyacağınızdan emin değilsiniz. Olay yöneticileri mi yoksa Efektler mi kullanmalısınız? Bu soruyu her yanıtlamanız gerektiğinde, neden kodun çalışması gerektiğini düşünün.
Olay yöneticileri belirli etkileşimlere yanıt olarak çalışır
Kullanıcının bakış açısına göre, bir mesajın gönderilmesi belirli bir “Gönder” düğmesine tıklandığı için olmalıdır. Mesajlarını başka bir zamanda veya başka bir nedenle gönderirseniz kullanıcı oldukça üzülecektir. İşte bu yüzden mesaj gönderme bir olay yöneticileri olmalıdır. Olay yöneticileri belirli etkileşimleri ele almanızı sağlar:
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
// ...
function handleSendClick() {
sendMessage(message);
}
// ...
return (
<>
<input value={message} onChange={e => setMessage(e.target.value)} />
<button onClick={handleSendClick}>Gönder</button>;
</>
);
}
Bir olay yöneticileri ile sendMessage(message)
ın sadece kullanıcı düğmeye bastığında çalışacağından emin olabilirsiniz.
Senkronizasyon gerektiğinde Efektler çalışır
Bileşeni sohbet odasına bağlı tutmanız gerektiğini de hatırlayın. Bu kod nereye gidecek?
Bu kodu çalıştırmak için neden belirli bir etkileşim değildir. Kullanıcının sohbet odası ekranına neden veya nasıl gittiği önemli değildir. Artık ona baktıklarına ve onunla etkileşime girebildiklerine göre, bileşenin seçilen sohbet sunucusuna bağlı kalması gerekir. Sohbet odası bileşeni uygulamanızın ilk ekranı olsa ve kullanıcı hiçbir etkileşim gerçekleştirmemiş olsa bile, yine de bağlanmanız gerekir. İşte bu yüzden bir Efekttir:
function ChatRoom({ roomId }) {
// ...
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]);
// ...
}
Bu kod sayesinde, kullanıcı tarafından gerçekleştirilen belirli etkileşimlerden bağımsız olarak, seçili sohbet sunucusuyla her zaman aktif bir bağlantı olduğundan emin olabilirsiniz. Kullanıcı ister sadece uygulamanızı açmış, ister farklı bir oda seçmiş ya da başka bir ekrana gidip geri dönmüş olsun, Efektiniz bileşenin o anda seçili olan odayla senkronize kalmasını ve gerektiğinde yeniden bağlanmasını sağlar.
import { useState, useEffect } from 'react'; import { createConnection, sendMessage } from './chat.js'; const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => connection.disconnect(); }, [roomId]); function handleSendClick() { sendMessage(message); } return ( <> <h1>{roomId} odasına hoş geldiniz!</h1> <input value={message} onChange={e => setMessage(e.target.value)} /> <button onClick={handleSendClick}>Gönder</button> </> ); } export default function App() { const [roomId, setRoomId] = useState('genel'); const [show, setShow] = useState(false); return ( <> <label> Sohbet odasını seçin:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="genel">genel</option> <option value="seyahat">seyahat</option> <option value="müzik">müzik</option> </select> </label> <button onClick={() => setShow(!show)}> {show ? 'Sohbeti kapat' : 'Sohbeti aç'} </button> {show && <hr />} {show && <ChatRoom roomId={roomId} />} </> ); }
Reaktif değerler ve reaktif mantık
Sezgisel olarak, olay yöneticilerinin her zaman “manuel” olarak tetiklendiğini söyleyebilirsiniz, örneğin bir düğmeye tıklayarak. Öte yandan, Efektler “otomatiktir”: senkronize kalmak için gerektiği sıklıkta çalışır ve yeniden çalışırlar.
Bunu düşünmenin daha kesin bir yolu vardır.
Bileşeninizin gövdesi içinde bildirilen prop’lar, durum ve değişkenler reaktif değerler olarak adlandırılır. Bu örnekte, serverUrl
reaktif bir değer değildir, ancak roomId
ve message
reaktif değerlerdir. Oluşturma veri akışına katılırlar:
const serverUrl = 'https://localhost:1234';
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
// ...
}
Bunlar gibi reaktif değerler yeniden oluşturma nedeniyle değişebilir. Örneğin, kullanıcı message
ı düzenleyebilir veya bir açılır menüde farklı bir roomId
seçebilir. Olay yöneticileri ve Efektler değişikliklere farklı şekilde yanıt verir:
- Olay yöneticilerinin içindeki mantık * reaktif değildir.* Kullanıcı aynı etkileşimi (örneğin bir tıklama) tekrar gerçekleştirmedikçe tekrar çalışmayacaktır. Olay yöneticileri, değişikliklerine “tepki vermeden” reaktif değerleri okuyabilir.
- Efektlerin içindeki mantık reaktiftir. Efektiniz reaktif bir değeri okuyorsa, bunu bir bağımlılık olarak belirtmeniz gerekir Ardından, bir yeniden oluşturma bu değerin değişmesine neden olursa, React, Efektinizin mantığını yeni değerle yeniden çalıştıracaktır.
Bu farkı göstermek için bir önceki örneğe geri dönelim.
Olay yöneticileri içindeki mantık reaktif değildir
Şu kod satırına bir göz atın. Bu mantık reaktif olmalı mı olmamalı mı?
// ...
sendMessage(message);
// ...
Kullanıcının bakış açısından, message
’da yapılan bir değişiklik, mesaj göndermek istedikleri anlamına gelmez. Bu sadece kullanıcının yazmakta olduğu anlamına gelir. Başka bir deyişle, mesaj gönderen mantık reaktif olmamalıdır. Sadece reactive value değiştiği için tekrar çalışmamalıdır. Bu yüzden olay yöneticisine aittir:
function handleSendClick() {
sendMessage(message);
}
Olay yöneticileri reaktif değildir, bu nedenle sendMessage(message)
yalnızca kullanıcı Gönder düğmesine tıkladığında çalışacaktır.
Efektlerin içindeki mantık reaktiftir
Şimdi bu satırlara geri dönelim:
// ...
const connection = createConnection(serverUrl, roomId);
connection.connect();
// ...
Kullanıcının bakış açısından, roomId
’deki bir değişiklik farklı bir odaya bağlanmak istedikleri anlamına gelir. Başka bir deyişle, odaya bağlanma mantığı reaktif olmalıdır. Bu kod satırlarının reaktif değere “ayak uydurmasını” ve bu değer farklıysa yeniden çalışmasını istiyorsunuz. Bu yüzden bir Efekte aittir:
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect()
};
}, [roomId]);
Efektler reaktiftir, bu nedenle createConnection(serverUrl, roomId)
ve connection.connect()
, roomId
nin her farklı değeri için çalışacaktır. Efektiniz sohbet bağlantısını o anda seçili olan odayla senkronize tutar.
Reaktif olmayan mantığı Efektlerden çıkarma
Reaktif mantığı reaktif olmayan mantıkla karıştırmak istediğinizde işler daha da zorlaşır.
Örneğin, kullanıcı sohbete bağlandığında bir bildirim göstermek istediğinizi düşünün. Bildirimi doğru renkte gösterebilmek için mevcut temayı (koyu veya açık) prop’lardan okursunuz:
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
showNotification('Connected!', theme);
});
connection.connect();
// ...
Ancak, theme
reaktif bir değerdir (yeniden oluşturma sonucunda değişebilir) ve bir Efekt tarafından okunan her reaktif değerin bağımlılığı olarak bildirilmesi gerekir Şimdi theme
Efektinizin bir bağımlılığı olarak belirtmeniz gerekir:
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
showNotification('Bağlandı!', theme);
});
connection.connect();
return () => {
connection.disconnect()
};
}, [roomId, theme]); // ✅ Tüm bağımlılıklar bildirildi
// ...
Bu örnekle oynayın ve bu kullanıcı deneyimindeki sorunu tespit edip edemeyeceğinizi görün:
import { useState, useEffect } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId, theme }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.on('connected', () => { showNotification('Baglandi!', theme); }); connection.connect(); return () => connection.disconnect(); }, [roomId, theme]); return <h1>{roomId} odasına hoş geldiniz!</h1> } export default function App() { const [roomId, setRoomId] = useState('genel'); const [isDark, setIsDark] = useState(false); return ( <> <label> Sohbet odasını seçin:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="genel">genel</option> <option value="seyahat">seyahat</option> <option value="müzik">müzik</option> </select> </label> <label> <input type="checkbox" checked={isDark} onChange={e => setIsDark(e.target.checked)} /> Koyu tema kullanın </label> <hr /> <ChatRoom roomId={roomId} theme={isDark ? 'dark' : 'light'} /> </> ); }
RoomId
değiştiğinde, sohbet beklediğiniz gibi yeniden bağlanır. Ancak theme
de bir bağımlılık olduğundan, koyu ve açık tema arasında her geçiş yaptığınızda sohbet ayrıca yeniden bağlanır. Bu hiç de iyi değil!
Başka bir deyişle, bir Efektin (reaktif olan) içinde olmasına rağmen bu satırın reaktif olmasını istemezsiniz:
// ...
showNotification('Bağlandı!', theme);
// ...
Bu reaktif olmayan mantığı, etrafındaki reaktif Efektten ayırmak için bir yola ihtiyacınız var.
Bir Efekt Olayı Bildirme
Bu reaktif olmayan mantığı Efektinizden çıkarmak için useEffectEvent
adlı özel bir Hook kullanın:
import { useEffect, useEffectEvent } from 'react';
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Baglandi!', theme);
});
// ...
Burada, onConnected
bir Efekt olayı olarak adlandırılır. Efekt mantığınızın bir parçasıdır, ancak daha çok bir olay yöneticisi gibi davranır. İçindeki mantık reaktif değildir ve her zaman sahne ve durumunuzun en son değerlerini “görür”.
Artık onConnected
Efekt olayını Efektinizin içinden çağırabilirsiniz:
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Baglandi!', theme);
});
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.on('connected', () => {
onConnected();
});
connection.connect();
return () => connection.disconnect();
}, [roomId]); // ✅ Tüm bagimliliklar bildirildi
// ...
Bu sorunu çözer. Efektinizin bağımlılıkları listesinden onConnected
öğesini kaldırmanız gerektiğini unutmayın. Efekt olayları reaktif değildir ve bağımlılıklardan çıkarılmalıdır.
Yeni davranışın beklediğiniz gibi çalıştığını doğrulayın:
import { useState, useEffect } from 'react'; import { experimental_useEffectEvent as useEffectEvent } from 'react'; import { createConnection, sendMessage } from './chat.js'; import { showNotification } from './notifications.js'; const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId, theme }) { const onConnected = useEffectEvent(() => { showNotification('Baglandi!', theme); }); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.on('connected', () => { onConnected(); }); connection.connect(); return () => connection.disconnect(); }, [roomId]); return <h1>{roomId} odasına hoş geldiniz!</h1> } export default function App() { const [roomId, setRoomId] = useState('genel'); const [isDark, setIsDark] = useState(false); return ( <> <label> Sohbet odasını seçin:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="genel">genel</option> <option value="seyahat">seyahat</option> <option value="müzik">müzik</option> </select> </label> <label> <input type="checkbox" checked={isDark} onChange={e => setIsDark(e.target.checked)} /> Koyu tema kullanın </label> <hr /> <ChatRoom roomId={roomId} theme={isDark ? 'dark' : 'light'} /> </> ); }
Efekt olaylarını olay yöneticilerine çok benzer olarak düşünebilirsiniz. Temel fark, olay işleyicilerinin kullanıcı etkileşimlerine yanıt olarak çalışması, Efekt olaylarının ise sizin tarafınızdan Efektlerden tetiklenmesidir. Efekt olayları, Efektlerin tepkiselliği ile tepkisel olmaması gereken kod arasındaki “zinciri kırmanızı” sağlar.
Efekt olayları ile en son propları ve state okuma
Efekt olayları, bağımlılık bağlayıcısını bastırmak isteyebileceğiniz birçok modeli düzeltmenize olanak tanır.
Örneğin, sayfa ziyaretlerini günlüğe kaydetmek için bir Efektiniz olduğunu varsayalım:
function Page() {
useEffect(() => {
logVisit();
}, []);
// ...
}
Daha sonra sitenize birden fazla rota eklersiniz. Şimdi Page
bileşeniniz geçerli yolu içeren bir url
prop alır. url
i logVisit
çağrınızın bir parçası olarak iletmek istiyorsunuz, ancak bağımlılık linter`ı şikayet ediyor:
function Page({ url }) {
useEffect(() => {
logVisit(url);
}, []); // 🔴 React Hook useEffect'in eksik bir bağımlılığı var: 'url'
// ...
}
Kodun ne yapmasını istediğinizi düşünün. Her URL farklı bir sayfayı temsil ettiğinden, farklı URL’ler için ayrı bir ziyareti günlüğe kaydetmek istiyorsunuz. Başka bir deyişle, bu logVisit
çağrısı url
ye göre reaktif olmalıdır. Bu nedenle, bu durumda, bağımlılık linter’ını takip etmek ve url
öğesini bir bağımlılık olarak eklemek mantıklıdır:
function Page({ url }) {
useEffect(() => {
logVisit(url);
}, [url]); // ✅ Tüm bagimliliklar bildirildi
// ...
}
Şimdi diyelim ki her sayfa ziyaretiyle birlikte alışveriş sepetindeki ürün sayısını da dahil etmek istiyorsunuz:
function Page({ url }) {
const { items } = useContext(ShoppingCartContext);
const numberOfItems = items.length;
useEffect(() => {
logVisit(url, numberOfItems);
}, [url]); // 🔴 React Hook useEffect'in eksik bir bağımlılığı var: 'numberOfItems'
// ...
}
Effect içinde numberOfItems
kullandınız, bu nedenle linter sizden bunu bir bağımlılık olarak eklemenizi istiyor. Ancak, logVisit
çağrısının numberOfItems
ile ilgili olarak reaktif olmasını istemezsiniz. Eğer kullanıcı alışveriş sepetine bir şey koyarsa ve sayıOfItems
değişirse, bu kullanıcının sayfayı tekrar ziyaret ettiği anlamına gelmez. Başka bir deyişle, sayfayı ziyaret etmek bir anlamda bir “olaydır”. Zaman içinde kesin bir anda gerçekleşir.
Kodu iki parçaya bölün:
function Page({ url }) {
const { items } = useContext(ShoppingCartContext);
const numberOfItems = items.length;
const onVisit = useEffectEvent(visitedUrl => {
logVisit(visitedUrl, numberOfItems);
});
useEffect(() => {
onVisit(url);
}, [url]); // ✅ Tüm bagimliliklar bildirildi
// ...
}
Burada, onVisit
bir Efekt olayıdır. İçindeki kod reaktif değildir. Bu nedenle numberOfItems
(veya başka herhangi bir reaktif değer!) kullanabilir ve bunun çevredeki kodun değişikliklerde yeniden yürütülmesine neden olacağından endişe duymazsınız.
Öte yandan, Efektin kendisi reaktif kalır. Efekt içindeki kod url
özelliğini kullanır, bu nedenle Efekt her yeniden oluşturmadan sonra farklı bir url
ile yeniden çalışacaktır. Bu da onVisit
Efekt olayını çağıracaktır.
Sonuç olarak, url
öğesindeki her değişiklik için logVisit
öğesini çağıracak ve her zaman en son numberOfItems
öğesini okuyacaksınız. Ancak, numberOfItems
kendi başına değişirse, bu kodun yeniden çalışmasına neden olmaz.
Derinlemesine İnceleme
Mevcut kod tabanlarında bazen lint kuralının bu şekilde bastırıldığını görebilirsiniz:
function Page({ url }) {
const { items } = useContext(ShoppingCartContext);
const numberOfItems = items.length;
useEffect(() => {
logVisit(url, numberOfItems);
// 🔴 Linteri bu şekilde bastırmaktan kaçının:
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [url]);
// ...
}
UseEffectEvent
React’in kararlı bir parçası haline geldikten sonra, kuralın asla bastırılmamasını öneriyoruz.
Kuralı bastırmanın ilk dezavantajı, Efektinizin kodunuza eklediğiniz yeni bir reaktif bağımlılığa “tepki vermesi” gerektiğinde React’in artık sizi uyarmayacak olmasıdır. Önceki örnekte, React size bunu yapmanızı hatırlattığı için bağımlılıklara url
eklediniz. Linter’ı devre dışı bırakırsanız, bu Efekt üzerinde gelecekte yapacağınız düzenlemeler için artık böyle hatırlatıcılar almayacaksınız. Bu da hatalara yol açar.
Burada, bağlayıcıyı bastırmanın neden olduğu kafa karıştırıcı bir hata örneği verilmiştir. Bu örnekte, handleMove
fonksiyonunun, noktanın imleci takip edip etmeyeceğine karar vermek için mevcut canMove
durum değişkeni değerini okuması gerekmektedir. Ancak, handleMove
içinde canMove
her zaman true
değerindedir.
Nedenini anlayabiliyor musunuz?
import { useState, useEffect } from 'react'; export default function App() { const [position, setPosition] = useState({ x: 0, y: 0 }); const [canMove, setCanMove] = useState(true); function handleMove(e) { if (canMove) { setPosition({ x: e.clientX, y: e.clientY }); } } useEffect(() => { window.addEventListener('pointermove', handleMove); return () => window.removeEventListener('pointermove', handleMove); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( <> <label> <input type="checkbox" checked={canMove} onChange={e => setCanMove(e.target.checked)} /> Noktanın hareket etmesine izin verilir </label> <hr /> <div style={{ position: 'absolute', backgroundColor: 'pink', borderRadius: '50%', opacity: 0.6, transform: `translate(${position.x}px, ${position.y}px)`, pointerEvents: 'none', left: -20, top: -20, width: 40, height: 40, }} /> </> ); }
Bu kodla ilgili sorun, bağımlılık linterinin bastırılmasıdır. Bastırmayı kaldırırsanız, bu Efektin handleMove
fonksiyonuna bağlı olması gerektiğini görürsünüz. Bu mantıklıdır: handleMove
bileşen gövdesi içinde bildirilir, bu da onu reaktif bir değer yapar. Her reaktif değer bir bağımlılık olarak belirtilmelidir, aksi takdirde zaman içinde eskimesi olasıdır!
Orijinal kodun yazarı, Effect’in herhangi bir reaktif değere bağlı olmadığını ([]
) söyleyerek React’e “yalan söylemiştir”. Bu nedenle React, canMove
değiştikten sonra (ve onunla birlikte handleMove
) Efekti yeniden senkronize etmedi. React, Efekti yeniden senkronize etmediği için, dinleyici olarak eklenen handleMove
, ilk render sırasında oluşturulan handleMove
fonksiyonudur. İlk render sırasında canMove
true
idi, bu yüzden ilk renderdan handleMove
sonsuza kadar bu değeri görecektir.
Linter’ı asla bastırmazsanız, eski değerlerle ilgili sorunları asla görmezsiniz.
UseEffectEvent
ile linter`a “yalan söylemeye” gerek yoktur ve kod beklediğiniz gibi çalışır:
import { useState, useEffect } from 'react'; import { experimental_useEffectEvent as useEffectEvent } from 'react'; export default function App() { const [position, setPosition] = useState({ x: 0, y: 0 }); const [canMove, setCanMove] = useState(true); const onMove = useEffectEvent(e => { if (canMove) { setPosition({ x: e.clientX, y: e.clientY }); } }); useEffect(() => { window.addEventListener('pointermove', onMove); return () => window.removeEventListener('pointermove', onMove); }, []); return ( <> <label> <input type="checkbox" checked={canMove} onChange={e => setCanMove(e.target.checked)} /> Noktanın hareket etmesine izin verilir </label> <hr /> <div style={{ position: 'absolute', backgroundColor: 'pink', borderRadius: '50%', opacity: 0.6, transform: `translate(${position.x}px, ${position.y}px)`, pointerEvents: 'none', left: -20, top: -20, width: 40, height: 40, }} /> </> ); }
Bu, useEffectEvent
in her zaman doğru çözüm olduğu anlamına gelmez. Bunu yalnızca reaktif olmasını istemediğiniz kod satırlarına uygulamalısınız. Yukarıdaki sanal alanda, Efekt kodunun canMove
ile ilgili olarak reaktif olmasını istemediniz. Bu yüzden bir Efekt olayı çıkarmak mantıklı oldu.
Linteri bastırmanın diğer doğru alternatifleri için Efekt Bağımlılıklarını Kaldırma bölümünü okuyun.
Efekt Olaylarının Sınırlamaları
Efekt Olayları, kullanma şekliniz açısından oldukça sınırlıdır:
- Sadece Efektlerin içinden çağırın.
- Asla diğer bileşenlere veya Hook’lara aktarmayın.
Örneğin, bir Efekt olayını şu şekilde bildirmeyin ve geçirmeyin:
function Timer() {
const [count, setCount] = useState(0);
const onTick = useEffectEvent(() => {
setCount(count + 1);
});
useTimer(onTick, 1000); // 🔴 Kaçının: Efekt olaylarini geçmek
return <h1>{count}</h1>
}
function useTimer(callback, delay) {
useEffect(() => {
const id = setInterval(() => {
callback();
}, delay);
return () => {
clearInterval(id);
};
}, [delay, callback]); // Bağımlılıklarda "callback" fonksiyonunu belirtmeniz gerekiyor
}
Bunun yerine, her zaman Efekt olaylarını doğrudan onları kullanan Efektlerin yanında bildirin:
function Timer() {
const [count, setCount] = useState(0);
useTimer(() => {
setCount(count + 1);
}, 1000);
return <h1>{count}</h1>
}
function useTimer(callback, delay) {
const onTick = useEffectEvent(() => {
callback();
});
useEffect(() => {
const id = setInterval(() => {
onTick(); // ✅ İyi: Yalnızca bir Efektin içinde yerel olarak çağrılır
}, delay);
return () => {
clearInterval(id);
};
}, [delay]); // Bağımlılık olarak "onTick" (bir Efekt olayı) belirtmeye gerek yok
}
Efekt olayları, Efekt kodunuzun reaktif olmayan “parçalarıdır”. Kendilerini kullanan Efektin yanında olmalıdırlar.
Özet
- Olay yöneticileri belirli etkileşimlere yanıt olarak çalışır.
- Efektler, senkronizasyon gerektiğinde çalışır.
- Olay yöneticilerinin içindeki mantık reaktif değildir.
- Efektlerin içindeki mantık reaktiftir.
- Reaktif olmayan mantığı Efektlerden Efekt olaylarına taşıyabilirsiniz.
- Efekt olaylarını yalnızca Efektlerin içinden çağırın.
- Efekt olaylarını diğer bileşenlere veya Hook’lara aktarmayın.
Problem 1 / 4: Güncellenmeyen bir değişkeni düzeltme
Bu Timer
bileşeni her saniye artan bir count
durum değişkenini tutar. Artan değer increment
durum değişkeninde saklanır. Artı ve eksi düğmeleriyle increment
değişkenini kontrol edebilirsiniz.
Ancak, artı düğmesine kaç kez tıklarsanız tıklayın, sayaç yine de her saniye bir artar. Bu kodda yanlış olan nedir? Efektin kodu içinde increment
neden her zaman 1
e eşittir? Hatayı bulun ve düzeltin.
import { useState, useEffect } from 'react'; export default function Timer() { const [count, setCount] = useState(0); const [increment, setIncrement] = useState(1); useEffect(() => { const id = setInterval(() => { setCount(c => c + increment); }, 1000); return () => { clearInterval(id); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( <> <h1> Sayaç: {count} <button onClick={() => setCount(0)}>Reset</button> </h1> <hr /> <p> Saniyedeki artış miktari: <button disabled={increment === 0} onClick={() => { setIncrement(i => i - 1); }}>–</button> <b>{increment}</b> <button onClick={() => { setIncrement(i => i + 1); }}>+</button> </p> </> ); }