React'in Kullanımının Nasıl Evcilleştirileceğini Öğrenmek Geri Çağırma Kancası
Yayınlanan: 2022-04-27React.js'nin son yıllarda oldukça popüler hale geldiği bir sır değil. Artık Facebook ve WhatsApp da dahil olmak üzere internetin en önde gelen oyuncularının çoğu için tercih edilen JavaScript kitaplığı.
Yükselişinin ana nedenlerinden biri, 16.8 sürümünde kancaların tanıtılmasıydı. React kancaları, sınıf bileşenleri yazmadan React işlevselliğinden yararlanmanıza olanak tanır. Artık kancalı işlevsel bileşenler, geliştiricilerin React ile çalışmak için başvurduğu yapı haline geldi.
Bu blog yazısında, belirli bir kancayı daha derine ineceğiz - useCallback
- çünkü işlevsel programlamanın memoization olarak bilinen temel bir bölümüne değiniyor. useCallback
kancasını nasıl ve ne zaman kullanacağınızı tam olarak bileceksiniz ve performans artırma özelliklerinden en iyi şekilde yararlanacaksınız.
Hazır? Hadi dalalım!
Memoization Nedir?
Notlandırma, karmaşık bir işlevin çıktısını depolamasıdır, böylece bir dahaki sefere aynı girdiyle çağrılır. Önbelleğe almaya benzer, ancak daha yerel düzeyde. Herhangi bir karmaşık hesaplamayı atlayabilir ve çıktıyı daha önce hesaplandığı için daha hızlı döndürebilir.
Bunun bellek ayırma ve performans üzerinde önemli bir etkisi olabilir ve bu zorlama, useCallback
kancasının hafifletmek istediği şeydir.
React'in useCallback ve useMemo karşılaştırması
Bu noktada, useCallback
useMemo
başka bir kanca ile güzel bir şekilde eşleştiğini belirtmekte fayda var. Her ikisini de tartışacağız, ancak bu bölümde ana konu olarak useCallback
odaklanacağız.
Temel fark, useMemo
not alınmış bir değer döndürmesi, useCallback
ise not alınmış bir işlev döndürmesidir. Bu, useMemo
hesaplanan bir değeri depolamak için kullanıldığı, useCallback
daha sonra arayabileceğiniz bir işlev döndürdüğü anlamına gelir.
Bu kancalar, bağımlılıklarından biri (örn. durum veya donanım) değişmedikçe size önbelleğe alınmış bir sürümü geri verecektir.
Şimdi iki işlevi çalışırken inceleyelim:
import { useMemo, useCallback } from 'react' const values = [3, 9, 6, 4, 2, 1] // This will always return the same value, a sorted array. Once the values array changes then this will recompute. const memoizedValue = useMemo(() => values.sort(), [values]) // This will give me back a function that can be called later on. It will always return the same result unless the values array is modified. const memoizedFunction = useCallback(() => values.sort(), [values])
Yukarıdaki kod parçacığı, yapmacık bir örnektir ancak iki geri arama arasındaki farkı gösterir:
-
memoizedValue
[1, 2, 3, 4, 6, 9]
dizisi olacak. Değerler değişkeni kaldığı sürecememoizedValue
da kalır ve asla yeniden hesaplama yapmaz. -
memoizedFunction
,[1, 2, 3, 4, 6, 9]
dizisini döndüren bir işlev olacaktır.
Bu iki geri aramanın harika yanı, önbelleğe alınmaları ve bağımlılık dizisi değişene kadar takılmalarıdır. Bu, bir işlemede çöp toplanmayacakları anlamına gelir.
Oluşturma ve Tepki
React söz konusu olduğunda not alma neden önemlidir?
React'in bileşenlerinizi nasıl oluşturduğuyla ilgili. React, verileri karşılaştırmak ve neyin güncelleneceğine karar vermek için bellekte depolanan bir Sanal DOM kullanır.
Sanal DOM, performansla React'e yardımcı olur ve uygulamanızın hızlı kalmasını sağlar. Varsayılan olarak, bileşeninizdeki herhangi bir değer değişirse, bileşenin tamamı yeniden oluşturulacaktır. Bu, React'i kullanıcı girişine "reaktif" hale getirir ve sayfayı yeniden yüklemeden ekranın güncellenmesini sağlar.
Değişiklikler o bileşeni etkilemeyeceği için bileşeninizi oluşturmak istemezsiniz. useCallback
ve useMemo
aracılığıyla not almanın kullanışlı olduğu yer burasıdır.
React, bileşeninizi yeniden oluşturduğunda, bileşeninizin içinde bildirdiğiniz işlevleri de yeniden oluşturur.
Bir fonksiyonun eşitliğini başka bir fonksiyonla karşılaştırırken her zaman yanlış olacağını unutmayın. Bir işlev aynı zamanda bir nesne olduğundan, yalnızca kendisine eşit olacaktır:
// these variables contain the exact same function but they are not equal const hello = () => console.log('Hello Matt') const hello2 = () => console.log('Hello Matt') hello === hello2 // false hello === hello // true
Başka bir deyişle, React bileşeninizi yeniden oluşturduğunda, bileşeninizde bildirilen tüm işlevleri yeni işlevler olarak görecektir.
Bu çoğu zaman iyidir ve basit işlevlerin hesaplanması kolaydır ve performansı etkilemez. Ancak işlevin yeni bir işlev olarak görülmesini istemediğiniz diğer zamanlarda, size yardımcı olması için useCallback
güvenebilirsiniz.
“Bir fonksiyonun yeni bir fonksiyon olarak görülmesini ne zaman istemem?” diye düşünüyor olabilirsiniz. Eh, useCallback
daha anlamlı olduğu bazı durumlar vardır:
- İşlevi, ayrıca not alınan başka bir bileşene geçiriyorsunuz (
useMemo
) - İşlevinizin hatırlaması gereken bir dahili durumu var
- İşleviniz, örneğin
useEffect
gibi başka bir kancanın bağımlılığıdır.
React useCallback'in Performans Avantajları
useCallback
uygun şekilde kullanıldığında, uygulamanızı hızlandırmaya ve gerekmedikçe bileşenlerin yeniden oluşturulmasını önlemeye yardımcı olabilir.
Örneğin, büyük miktarda veri alan ve bu verileri aşağıdaki gibi bir grafik veya grafik biçiminde görüntülemekten sorumlu bir bileşeniniz olduğunu varsayalım:
Veri görselleştirmenizin bileşeninin ana bileşeninin yeniden oluşturulduğunu, ancak değiştirilen aksesuarlar veya durumun bu bileşeni etkilemediğini varsayalım. Bu durumda, muhtemelen onu yeniden oluşturmak ve tüm verileri yeniden almak istemezsiniz veya buna ihtiyaç duymazsınız. Bu yeniden oluşturma ve yeniden getirme işleminden kaçınmak, kullanıcınızın bant genişliğinden tasarruf edebilir ve daha sorunsuz bir kullanıcı deneyimi sağlayabilir.
React useCallback'in Dezavantajları
Bu kanca, performansı artırmanıza yardımcı olsa da, tuzaklarıyla birlikte gelir. useCallback
(ve useMemo
) kullanmadan önce göz önünde bulundurulması gereken bazı şeyler şunlardır:
- Çöp toplama: Henüz not alınmamış diğer işlevler, belleği boşaltmak için React tarafından atılır.
- Bellek ayırma: Çöp toplamaya benzer şekilde, ne kadar çok not alınmış işleve sahipseniz, o kadar fazla bellek gerekir. Ayrıca, bu geri aramaları her kullandığınızda, React içinde size önbelleğe alınmış çıktıyı sağlamak için daha fazla bellek kullanması gereken bir sürü kod vardır.
- Kod karmaşıklığı: Bu kancalara işlevleri sarmaya başladığınızda, kodunuzun karmaşıklığını hemen artırırsınız. Artık bu kancaların neden kullanıldığının daha iyi anlaşılmasını ve bunların doğru şekilde kullanıldığının onaylanmasını gerektiriyor.
Yukarıdaki tuzakların farkında olmak, onları kendi başınıza tökezleme baş ağrısından kurtarabilir. useCallback
kullanmayı düşünürken, performans avantajlarının dezavantajlardan daha ağır basacağından emin olun.
React useCallback Örneği
Aşağıda bir Düğme bileşeni ve bir Sayaç bileşeni ile basit bir kurulum yer almaktadır. Sayacın iki durumu vardır ve her biri Sayaç bileşenleri durumunun ayrı bir bölümünü güncelleyecek olan iki Düğme bileşeni oluşturur.
Button bileşeni iki sahne alır: handleClick
ve name. Düğme her işlendiğinde konsolda oturum açacaktır.
import { useCallback, useState } from 'react' const Button = ({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> } const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) return ( <> {countOne} {countTwo} <Button handleClick={() => setCountOne(countOne + 1)} name="button1" /> <Button handleClick={() => setCountTwo(countTwo + 1)} name="button1" /> </> ) }
Bu örnekte, herhangi bir düğmeyi her tıkladığınızda konsolda şunu göreceksiniz:
// counter rendered // button1 rendered // button2 rendered
Şimdi, handleClick
işlevlerimize useCallback
uygularsak ve Button'ımızı React.memo içine React.memo
, useCallback
bize ne sağladığını görebiliriz. React.memo
, useMemo
benzer ve bir bileşeni not etmemize izin verir.
import { useCallback, useState } from 'react' const Button = React.memo(({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> }) const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne) const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo]) return ( <> {countOne} {countTwo} <Button handleClick={memoizedSetCountOne} name="button1" /> <Button handleClick={memoizedSetCountTwo} name="button1" /> </> ) }
Şimdi bu butonlardan birine tıkladığımızda sadece konsola giriş yapmak için tıkladığımız butonu göreceğiz:
// counter rendered // button1 rendered // counter rendered // button2 rendered
Düğme bileşenimize notlandırma uyguladık ve ona iletilen prop değerleri eşit olarak görülüyor. İki handleClick
işlevi önbelleğe alınır ve bağımlılık dizisindeki bir öğenin değeri değişene kadar React tarafından aynı işlev olarak görülecektir (örneğin, countOne
, countTwo
).
Özet
useCallback
ve useMemo
ne kadar havalı olsa da, belirli kullanım durumları olduğunu unutmayın - her işlevi bu kancalarla sarmalamamalısınız. İşlev hesaplama açısından karmaşıksa, not alınmış bir bileşene geçirilen başka bir kancanın veya bir desteğin bağımlılığı, useCallback
için ulaşmak isteyebileceğiniz iyi göstergelerdir.
Bu makalenin, bu gelişmiş React işlevselliğini anlamanıza ve bu süreçte işlevsel programlama konusunda daha fazla güven kazanmanıza yardımcı olduğunu umuyoruz!