Bir React bileşeni, kendi state'i değiştiğinde, ebeveyni yeniden render olduğunda ya da okuduğu bir context değiştiğinde yeniden render olur. Render, React'in taze JSX almak için bileşen fonksiyonunu yeniden çağırması demektir; ardından sayfanın yalnızca gerçekten değişen kısımlarını günceller.
Önizlemenin altındaki Console'u aç ve düğmeye tıkla. Hiç props almayan Title dahil üç bileşen de kayıt yazar. Ebeveyni render olduğu için render oldu.
Bir render'ı ne tetikler
React bir bileşeni tam olarak şu nedenlerle render eder:
- İlk render. Uygulama başlar ya da bileşen ağaçta ilk kez görünür.
- State'i değişti.
useState'ten bir setter'ı ya dauseReducer'dan bir dispatch'i yeni bir değerle çağırdın. - Ebeveyni render oldu. Varsayılan olarak bir bileşen render olduğunda, döndürdüğü her bileşen de en alta kadar render olur.
- Okuduğu bir context değişti.
useContext(SomeContext)çağıran bir bileşen, en yakın provider yeni birvalueaktardığında render olur (bkz. useContext).
Yaygın bir inanış, bir bileşenin "props'u değiştiği için" render olduğudur. Bu nedeni tersine çevirir. Props ebeveynin render ederken aktardığı argümanlardır, bu yüzden yeni props ancak ebeveyn render olduğunda gelebilir. Ve ebeveynin render olması kendi başına yeterlidir: yukarıdaki Title'ın props'u yoktur ve yine de her seferinde render olur.
State'i zaten sahip olduğu değere ayarlamak (Object.is ile karşılaştırılır) çocukların render'ını başlatmaz. React hiçbir şeyin değişmediğini fark etmeden önce o tek bileşeni bir kez çağırabilir, ama sonucu atar.
Render ve commit
Her güncelleme iki aşamadan geçer:
- Render. React bileşenlerini çağırır. Bileşenler JSX döndürür; bu sadece ekranın ne göstermesi gerektiğini tarif eden nesnelerdir. Sayfada henüz hiçbir şey değişmez; render'ın saf olması gerekmesinin nedeni budur: DOM yazımı yok, istek yok, bileşen dışındaki değişkenleri değiştirmek yok.
- Commit. React yeni çıktıyı öncekiyle karşılaştırır ve farkları DOM'a uygular: yalnızca değişen düğümleri ekler, kaldırır ya da günceller. Sonra tarayıcı boyar ve ondan sonra React efektlerini çalıştırır.
Aşağıdaki örnek her tıklamada render olur, ama React aynı <input> öğesini korur. Her commit'ten sonra bir efekt çalışır ve bunu kontrol eder.
Input'a bir şey yaz ve birkaç kez tıkla. Yazdığın metin kalır, çünkü React input'u hiç değiştirmedi: tıklama başına tek DOM değişikliği <p> içindeki sayıdır. Kayıt sırayı da gösterir: önce render, commit'ten sonra efekt.
Sanal DOM, sade bir dille
"Sanal DOM" (virtual DOM), bileşenlerinin döndürdüğü nesnelerin popüler adıdır. <p>Items: {count}</p>, { type: 'p', props: { children: ['Items: ', 1] } } gibi bir nesne oluşturan bir çağrıya derlenir. Bir render'dan sonra React bu nesnelerden oluşan yeni ağaçta öncekiyle yan yana dolaşır; tip ve konumun eşleştiği yerde mevcut DOM düğümünü korur ve yalnızca değişen özellikleri ve metni günceller. Bu karşılaştırmaya reconciliation denir.
Terim iki nedenle gevşektir. React DOM'un ikinci bir kopyasını tutup iki DOM'u karşılaştırmaz: öğe nesnelerini kendi iç bileşen ağacıyla (fiber ağacı) karşılaştırır. Ayrıca aynı süreç React Native gibi hiç DOM'u olmayan hedefleri de yönetir. React belgeleri bu ifadeden çoğunlukla kaçınır ve bunun yerine render ve commit'ten söz eder. Pratikte önemli olan sonuçtur: render, DOM işine kıyasla ucuzdur, çünkü çoğu render küçük bir DOM güncellemesiyle ya da hiç güncelleme olmadan biter.
Karşılaştırmanın nasıl çalıştığından iki kural çıkar. Aynı yerde farklı bir öğe tipi (bir <section> ile değiştirilen bir <div> ya da ComponentB ile değiştirilen ComponentA) eski alt ağacı ve state'ini yok eder. Listelerde ise key, hangi öğenin hangisi olduğunu React'e söyler, böylece düğümleri yeniden oluşturmak yerine taşıyabilir.
İhtiyacın olmayan render'ları durdurmak
Çoğu fazla render fark edebileceğin bir maliyete yol açmaz. Biri gerçekten zarar verdiğinde, örneğin sayfanın başka bir yerindeki her tuş vuruşunda render olan büyük bir liste, bunları sırayla dene.
State'i aşağı taşı
Ekranın yalnızca küçük bir kısmı bir state parçasını kullanıyorsa, o state'i yalnızca o kısmı saran bir bileşende tut. Bu sürüm her tuş vuruşunda ProductList'i yeniden render eder:
export default function App() {
const [text, setText] = useState('');
return (
<>
<input value={text} onChange={(e) => setText(e.target.value)} />
<ProductList />
</>
);
}
Input'u ve state'ini kendi bileşenlerine taşı; liste artık render olan bileşenin içinde değildir:
Birkaç harf yaz: yalnızca SearchBox kayıt yazar.
Bunun yerine children aktar
Bazen state'in pahalı kısmın etrafındaki bir sarmalayıcıda yaşaması gerekir, örneğin bir vurguyu açıp kapatan bir panel. Sarmalayıcının children kabul etmesini sağla. Çocuk öğeleri ebeveyn oluşturur ve sarmalayıcının state'i değiştiğinde ebeveyn render olmadığı için bu öğeler öncekiyle aynı nesnelerdir; bu yüzden React onları atlar.
Toggle'a tıkla; yalnızca Highlighter kayıt yazar. Şimdi <Article />'ı {children} yerine Highlighter'ın JSX'inin içine taşı; her tıklama render Article'ı da kaydeder.
Memoize et
Yeniden yapılandırmalardan hiçbiri uymadığında çocuğu memo ile sar. React o zaman props'unu öncekilerle karşılaştırır ve hepsi aynı olduğunda render'ı atlar. Render sırasında oluşturulan nesneler ve fonksiyonlar her seferinde yenidir, bu yüzden memo genellikle bu props için useMemo ya da useCallback ile birlikte gelir. React.memo sayfası bunu çalışan örneklerle gösteriyor.
import { memo } from 'react';
const ProductList = memo(function ProductList({ category }) {
// skipped while category stays the same
});
React Compiler bu tür memoization'ı derleme zamanında senin için ekleyebilir, ama state'i yeniden yapılandırmak hâlâ denenecek ilk şeydir: işi önbelleğe almak yerine ortadan kaldırır.
Render'lar genellikle sorun değildir
Bir render nesneler döndüren bir fonksiyon çağrısıdır. React bunlardan binlercesini hızla çalıştırır ve aynı çıktıyı üreten bir render hiçbir DOM'u değiştirmez. "Güvende olmak için" her yere memo ekleme: her karşılaştırmanın kendi maliyeti vardır ve kodu okumayı zorlaştırır. Önce ölç. React DevTools Profiler hangi bileşenlerin, neden render olduğunu ve her birinin ne kadar sürdüğünü gösterir.
Sıkça Sorulan Sorular
Bir React bileşeninin yeniden render olmasına ne yol açar?
Üç şey: kendi state'i değişir, ebeveyni render olur ya da useContext ile okuduğu bir context yeni bir değer alır. Props ayrı bir tetikleyici değildir: yeni props yalnızca ebeveyn render olduğu için gelir.
Ebeveyn yeniden render olduğunda çocuk da yeniden render olur mu?
Evet, varsayılan olarak render olan bir ebeveynin içindeki her bileşen, props'u değişmemiş olsa bile render olur. Çocuğu memo ile sarmak, props'u geçen seferkiyle aynı olduğunda React'in onu atlamasını sağlar.
React'te sanal DOM (virtual DOM) nedir?
Bileşenlerinin döndürdüğü düz JavaScript nesnelerinden (React öğeleri) oluşan ağacın gevşek bir adıdır. React yeni ağacı öncekiyle karşılaştırır ve yalnızca farklı olan gerçek DOM düğümlerini değiştirir.
Yeniden render performans için kötü mü?
Genellikle hayır. Bir render nesneler üreten bir fonksiyon çağrısıdır ve React DOM'a yalnızca çıktının değiştiği yerde dokunur. Bir render ölçülebilir şekilde yavaş olduğunda optimize et, örneğin React DevTools Profiler ile.
Render ile commit arasındaki fark nedir?
Render, React'in ekranın nasıl görünmesi gerektiğini bulmak için bileşenlerini çağırmasıdır. Commit, React'in farkları DOM'a uygulamasıdır. Aynı çıktıyı üreten bir render hiçbir DOM değişikliği commit etmez.