Skip to content

Yunus Emre Dilber

React Fiber Reconciliation

React9 min read

Giriş

React, kullanıcı arayüzleri geliştirebileceğiniz bir JavaScript kütüphanesidir. İçinde, bileşenlerin state değişikliklerini takip eden ve değişen kısımları güncelleyen bir mekanizma vardır. React'ta bu işlem reconciliation (uyumlaştırma) olarak adlandırılır. setState metodunu çağırdığımızda React, state veya prop'ların değişip değişmediğini kontrol edip, değişen kısımlar varsa bu kısımları yeniden render eder.

Reconcilation hakkında yüksek seviyede genel bir bilgi için, React dokümanındaki uyumlaştırma bölümünü okuyabilirsiniz.

render metodu çağırıldıktan sonra, elimize React elemanlarından oluşan bir ağaç geçer. Bu react elemanları ağacının yanı sıra, React'ın state'i korumak için bir iç nesne ağacı (bileşenler, DOM düğümleri, vb.) vardır. Versiyon 16'dan itibaren, React bu iç nesne ağacının yeni bir gerçekleştirimine geçti ve bunu yöneten algoritmanın kod adı Fiber'dir.

Bu yazıdaki anlattıklarım React'ı kullanmak için gerekli değildir. Ancak React'ın nasıl çalıştığını anlamak, React kullanırken yazdığınız kodun mantığını daha iyi kavramanızı sağlayabilir. Ayrıca, React'ın karmaşık problemler için uyguladığı çözümleri görmek de bakış açınızı genişletebilir. Ek olarak, React'a katkıda bulunmak gibi bir amacınız varsa, bu yazı size güzel bir kaynak olacaktır.

Kullanacağımız örnek

Yazının devamında aşağıdaki basit bileşen üzerinden konuşacağız:

0

Bu bileşen canlı olarak render edilmekte. İsterseniz kurcalayabilirisiniz.

React elemanları ve Fiber düğümleri

React'taki her bileşenin, render metodundan döndürülen bir görünümü veya şablonu diyebileceğimiz bir kullanıcı arayüzü temsili vardır. İşte örnek bileşenimizin şablonu:

1<button key="1" onClick={this.onClick}>Update counter</button>
2<span key="2" style={{ color: 'black' }}>{this.state.count}</span>

React elemanları

Bir şablon JSX derleyicisinden geçtiğinde, elimizde bir gurup React elemanı olur. JSX kullanmamız zorunlu olmadığından, örnek bileşenimiz için render metodunu şu şekilde yeniden yazabiliriz:

1render() {
2 return [
3 React.createElement(
4 'button',
5 {
6 key: '1',
7 onClick: this.onClick
8 },
9 'Update counter'
10 ),
11 React.createElement(
12 'span',
13 {
14 key: '2',
15 style: { color: 'black' }
16 },
17 this.state.count
18 )
19 ]
20}

React.createElement fonksiyonu çalıştıktan sonra render metodunun içi şu şekilde olacaktır:

1[
2 {
3 $$typeof: Symbol(react.element),
4 type: 'button',
5 key: "1",
6 props: {
7 children: 'Update counter',
8 onClick: () => { ... }
9 }
10 },
11 {
12 $$typeof: Symbol(react.element),
13 type: 'span',
14 key: "2",
15 props: {
16 children: 0,
17 style: { color: 'black' }
18 }
19 }
20]

React'ın bu nesnelere $$typeof özelliğini eklediğini görebilirsiniz. Genel olarak, bunun güvenlik için eklendiğini söyleyebilirim. $$typeof özelliği hakkında daha detaylı bilgiye buradan ulaşabilirsiniz.

type, key ve props özellikleri React.createElement fonksiyonuna geçtiğimiz parametrelerden geliyor. Tüm haline buradan bakabilirsiniz. ref gibi özellikler konumuzun dışında olduğu için atlıyorum.

ClickCounter bileşenimizin React elemanında herhangi bir state veya prop yoktur:

1{
2 $$typeof: Symbol(react.element),
3 key: null,
4 props: {},
5 type: ClickCounter
6}

Fiber düğümleri

Reconciliation sırasında render metodundan döndürülen her React elemanının verisi, fiber ağacıyla birleştirilir. Her React elemanına karşılık gelen bir fiber düğümü vardır. React elemanlarından farklı olarak, fiberler her render işleminde yeniden oluşturulmaz. Bunlar, bileşenlerin state'ini ve DOM'u tutan değiştirilebilir veri yapılarıdır.

React elemanının türüne bağlı olarak, farklı aktiviteler gerçekleştirilir. Örneğimizde, sınıf bileşeni ClickCounter için yaşam döngüsü metodları ve render metodu çağırılırken, span host bileşeni (DOM düğümü) için DOM mutasyonu gerçekleştirir. Bundan dolayı her bir React elemanı, yapılması gereken işi tanımlayan ilgili tipte bir Fiber düğümüne dönüştürülür.

Fiber'i, yapılması gereken iş birimini temsil eden bir veri yapısı olarak düşünebilirsiniz.

Fiber’in mimarisi ayrıca yapılacak işi izlemek, planlamak, duraklatmak ve iptal etmek için uygun bir yol sağlar.

Bir React elemanı ilk kez bir fiber düğüme dönüştürülürken, fiber düğümü oluşturmak için elemanın verileri kullanılarak createFiberFromTypeAndProps fonksiyonu çağırılır. Sonraki güncellemelerde, fiber düğümü sadece ona karşılık gelen React elemanının verisi kullanılarak gerekli özellikleri güncellenir. Ayrıca, key prop'una göre düğüm taşınır veya karşılık gelen React öğesi artık render metodundan döndürülmezse silinir.

React'ın mevcut fiber düğümleri için gerçekleştirdiği tüm etkinlikleri ve ilgili işlevleri görmek için ChildReconciler fonksiyonunu inceleyebilirsiniz.

Örneğimiz için, fiber ağacı şu şekilde görünür:

Tüm fiber düğümler, fiber düğümlerdeki şu özellikler kullanılarak bir linked list aracılığıyla bağlanır: child, sibling ve return (parent).

Fiber'in neden linked list kullandığı hakkında güzel bir yazıya şuradan ulaşabilirsiniz: The how and why on React’s usage of linked list in Fiber to walk the component’s tree

Current ve work in progress ağaçları

İlk render işleminden sonra React, kullanıcı arayüzünü oluşturmak için kullanılan ve uygulamanın durumunu yansıtan bir Fiber ağacı oluşturur. Bu ağaca genellikle current (şu anki) denir. React güncellemeler üzerinde çalışmaya başladığında, ekranın gelecekteki durumunu yansıtan bir workInProgress (yapım aşamasında) ağacı oluşturur.

Tüm çalışma workInProgress ağacındaki fiberler üzerinde gerçekleştirilir. React, current ağacında gezinirken, mevcut her bir fiber düğümü için workInProgress ağacını oluşturan alternatif bir düğüm oluşturur. Bu düğüm, render metodu tarafından döndürülen React elemanından alınan veriler kullanılarak oluşturulur. Güncellemeler işlendikten ve ilgili tüm çalışmalar tamamlandıktan sonra, React'in ekrana aktarılmaya hazır bir alternatif ağacı olacaktır. Bu ağaç (workInProgress) ekrana geçirildikten sonra, current ağaç olur.

React’in temel ilkelerinden biri tutarlılıktır. React, DOM'u her zaman tek seferde günceller ve kısmi sonuçlar göstermez. workInProgress ağacı, kullanıcı tarafından görülmeyen bir "taslak" görevi görür, böylece React önce tüm bileşenleri işleyebilir ve ardından değişiklikleri ekrana aktarabilir.

Kaynak kodların içinde, hem current hem de workInProgress ağaçlarından fiber düğümler alan birçok fonksiyon görebilirsiniz. İşte böyle bir fonksiyonun prototipi:

1function updateHostComponent(current, workInProgress, renderExpirationTime) {...}

Her bir fiber düğümü, diğer ağaçtan bir muadiline alternate (alternatif) alanında bir referans tutar. current ağaçtaki bir düğüm, workInProgress ağacındaki düğüme işaret eder ve bunun tersi de geçerlidir.

Fiber düğüm yapısı

Şimdi ClickCounter bileşeni ve span için oluşturulan fiber düğümlerin yapısına bakalım:

ClickCounter
1{
2 stateNode: new ClickCounter,
3 type: ClickCounter,
4 alternate: new FiberNode, /* tag: 1, stateNode: new ClickCounter */
5 key: null,
6 updateQueue: null,
7 memoizedState: {count: 0},
8 pendingProps: {},
9 memoizedProps: {},
10 tag: 1,
11 effectTag: 0,
12 nextEffect: null,
13 return: new FiberNode, /* tag: 8, stateNode: null */
14 child: new FiberNode, /* tag: 5, stateNode: button */
15 sibling: null
16}

span
1{
2 stateNode: new HTMLSpanElement,
3 type: "span",
4 alternate: new FiberNode, /* tag: 5, stateNode: "span" */
5 key: "2",
6 updateQueue: null,
7 memoizedState: null,
8 pendingProps: {children: 0},
9 memoizedProps: {children: 0},
10 tag: 5,
11 effectTag: 0,
12 nextEffect: null,
13 return: new FiberNode, /* tag: 1, stateNode: ClickCounter */
14 child: null,
15 sibling: null,
16}

Halihazırda oluşmuş neseneleri temsil etmek için new ClassName kalıbını kullandığıma dikkat edin.

Fiber düğümlerde oldukça fazla alan var. Önceki bölümlerde alternate alanlanının amacını açıkladım. Bir sonraki bölümde de, effectTag ve nextEffect alanlarını açıklayacağım. Şimdi, neden diğerlerine ihtiyacımız olduğunu görelim.

stateNode

Fiber düğümle ilişkilendirilmiş bir bileşenin, bir DOM düğümünün veya diğer React elemanı tipinin sınıf nesnesine ait referansı tutar. Genel olarak, bu özelliğin bir fiberle ilişkili yerel state'i tutmak için kullanıldığını söyleyebiliriz.

type

Bu fiber ile ilişkili fonksiyonu veya sınıfı tanımlar. Sınıf bileşenleri için yapıcı fonksiyona (constructor) işaret eder ve DOM öğeleri için HTML etiketini belirtir.

tag

Fiber tipini tanımlar. Reconciliation algoritmasında hangi işin yapılması gerektiğini belirlemek için kullanılır. Örneğimizde, ClickCounter bileşeninin tipi ClassComponent (1) ve span öğesininki HostComponent (5) dir.

updateQueue

State güncellemeleri, callbackler ve DOM güncellemelerinden oluşan bir kuyruk.

memoizedState

Çıktıyı oluşturmak için kullanılan fiberin state'i. Güncellemeleri işlerken, o anda ekranda oluşturulmakta olan state'i yansıtır.

memoizedProps

Önceki render sırasında çıktıyı oluşturmak için kullanılan fiberin prop'ları.

pendingProps

React elemanlarındaki yeni verilerle güncellenen ve alt bileşenlere veya DOM öğelerine uygulanması gereken prop'lar.

key

React'in bir grup çocuk içinden hangi öğelerin değiştiğini, listeye eklendiğini veya listeden kaldırıldığını anlamanıza yardımcı olacak benzersiz bir tanımlayıcı. Key hakkında daha fazla bilgi için dokümandaki Listeler ve Anahtarlar kısmına bakabilirsiniz.

return, child ve sibling

React'ın reconciliation sırasında, fiber düğümler arasında dolaşmasını sağlar. Aşağıda algoritmayı açıklarken daha net bir şekilde üzerlerinden geçeceğiz.

Fiber düğümün tüm yapısına buradan bulabilirsiniz. Konumuzun dışında olan Scheduler'e özgü expirationTime, childExpirationTime ve mode gibi alanları es geçtiğimi görebilirsiniz.

Bir fiber düğüm'e ulaşmak için Chrome'da aşağıdaki işlemi uygulayabilirisiniz (keşif ve dubug için):

Yan etkiler ve etki listesi

React'taki bir bileşeni, kullanıcı arayüzünün temsilini hesaplamak için state ve prop'ları kullanan bir fonksiyon olarak düşünebiliriz. DOM'u mutasyona geçirmek veya yaşam döngüsü yöntemlerini çağırmak gibi diğer tüm işlemler bir yan etki (side-effect) veya basitçe bir etki (effect) olarak düşünülmelidir.

Çoğu state ve prop güncellemesinin yan etkilere nasıl yol açtığını görebilirsiniz. Ve etki uygulamak/işlemek bir tür iş olduğundan, bir fiber düğüm, güncellemelere ek olarak etkileri izlemek için uygun bir mekanizmadır. Her fiber düğümün kendisiyle ilişkili etkileri olabilir. Bunlar, effectTag alanına kodlanırlar. Bu nedenle, Fiber'deki etkiler temel olarak güncellemeler işlendikten sonra yapılması gereken işleri tanımlar. DOM öğeleri için iş, öğe ekleme, güncelleme veya kaldırma işlemlerinden oluşur. Sınıf bileşenleri için React'in ref'leri güncellemesi ve componentDidMount ve componentDidUpdate yaşam döngüsü yöntemlerini çağırması gerekebilir. Diğer fiber türlerine karşılık gelen başka etkiler de vardır.

React, güncellemeleri çok hızlı bir şekilde işler ve bu performans seviyesine ulaşmak için birkaç ilginç teknik kullanır. Bunlardan biri, hızlı iterasyon için etkilere sahip olan doğrusal bir fiber düğüm listesi oluşturmasıdır. Doğrusal listede gezinmek bir ağaçtan çok daha hızlıdır ve yan etkileri olmayan düğümlerde zaman geçirmeye de gerek yoktur.

Bu listenin amacı, DOM güncellemelerine veya bunlarla ilişkili diğer etkilere sahip düğümleri işaretlemektir. Bu liste finishedWork ağacının bir alt kümesidir ve current ve workInProgress ağaçlarında kullanılan child özelliği yerine nextEffect özelliğini kullanarak diğer düğümlere bağlanır.

Genel algoritma

React, reconciliation (uyumlaştırma) işini iki ana aşamada gerçekleştirir: render ve commit.

İlk olarak, render aşamasında React, setState veya React.render aracılığıyla bileşenlere güncellemeler uygular ve kullanıcı arayüzünde nelerin güncellenmesi gerektiğini bulur. Eğer ilk render işlemi ise React, render metodundan döndürülen her öğe için yeni bir fiber düğümü oluşturur. Sonraki güncellemelerde, mevcut React elemanları için fiberler yeniden kullanılır ve güncellenir. Bu işlemin sonucu, yan etkilerle (side-effects) işaretlenmiş bir fiber düğüm ağacıdır. Etkiler, bir sonraki commit aşamasında yapılması gereken işi açıklar. Bu aşamada React, etkilerle işaretlenmiş bir fiber ağacı alır ve bunları nesnelere uygular. Etkiler listesini gözden geçirir ve DOM güncellemelerini ve kullanıcı tarafından görülebilen diğer değişiklikleri gerçekleştirir.

render aşamasında çalışmanın eşzamansız olarak gerçekleştirilebileceğini anlamak önemlidir. React, mevcut zamana bağlı olarak bir veya daha fazla fiber düğümü işleyebilir, daha sonra yapılan işi tutmak ve bir olaya izin vermek (kullanıcı'nın parola yazması gibi) için durabilir. Daha sonra kaldığı yerden devam eder. Bazen de, yapılan işten kurtulup tekrar üstten başlaması gerekebilir. Bu duraklamalar, gerçekleştirilen çalışmanın DOM güncellemeleri gibi kullanıcı tarafından görülebilir değişikliklere yol açmadığı için mümkün olur. Buna karşılık, commit aşaması her zaman eşzamanlıdır. Bunun nedeni, bu aşama sırasında yapılan çalışmanın kullanıcı tarafından görülebilir değişikliklere (DOM güncellemeleri gib) yol açmasıdır. React'ın bunu tek bir seferde yapmasının nedeni budur.

Yaşam döngüsü (lifecycle) metodlarını çağırmak, React tarafından gerçekleştirilen bir çalışma türüdür. Bazı metodlar render aşamasında, diğerleri ise commit aşamasında çağrılır. render aşamasında çalışırken çağrılan yaşam döngülerinin listesi:

  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render

componentWillMount, componentWillReceiveProps, componentWillUpdate metodları kullanımdan kaldırıldı. Bunun nedeni, render aşaması DOM güncellemeleri gibi yan etkiler üretmediğinden, React güncellemeleri bileşenlerle eşzamansız bir şekilde eşzamanlı olarak işleyebilir. "Bu metodlar yerine ne kullanmalıyız?" diyorsanız Update on Async Rendering yazsına göz atmak isteyebilirsiniz.

commit aşamasında çağırılan yaşam döngüsü metodlarının listesi:

  • getSnapshotBeforeUpdate
  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

Bu yöntemler eşzamanlı commit aşamasında yürütüldüğünden, yan etkiler içerebilir ve DOM'a dokunabilirler.

Şimdi ağaçta gezinmek ve iş yapmak için kullanılan genel algoritmaya göz atmak için arka planımız var. Hemen başlayalım.

Render aşaması

React'ın kalbinin attığı yer burasıdır diyebiliriz. Teknik kısma girmeden önce, sözde kod olarak algoritmanın nasıl gezindiğini tanımlayalım:

  1. Kök düğümünden başla.
  2. Çocuk olmayana kadar çocuğa git.
  3. Kardeş (sibling) varsa kardeşi seç ve 2'ye git.
  4. Hiçbiri yoksa, amcayı seç (ebeveyine gidip kardeşi seç) ve 2'den devam et. Bu adımda amca yoksa, amca bulunana kadar bir üst düğüme gidilir.
  5. Hiç ebeveyini olmayan düğüme (kök) geldin. Dur.

Aşağıdaki örnek ağacı ele alalım:

Bu örnekte gezme sırası (ilk gelme sırasına göre) şu şekide olacaktır:

A -> B -> C -> E -> G -> H -> D -> F

Reconciliation algoritması, renderRoot fonksiyonunu kullanarak her zaman en üstteki HostRoot fiber düğümünden başlar. Bununla birlikte React, tamamlanmış düğümleri hızlıca atlayarak işlenmemiş fiber düğümlere geçer.

Tüm fiber düğümler workLoop içinde işlenir. Döngünün senkron bölümünün uygulaması:

1function workLoop(isYieldy) {
2 if (!isYieldy) {
3 while (nextUnitOfWork !== null) {
4 nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
5 }
6 } else {...}
7}

Yukarıdaki kodda nextUnitOfWork, yapılması gereken iş olarak workInProgress ağacından bir fiber düğüme referans tutar. React, fiber ağacında dolaştıkça, bu değişkeni, bitmemiş işi olan başka bir fiber düğüm olup olmadığını bilmek için kullanır. Mevcut fiber işlendikten sonra değişken, ağaçtaki bir sonraki fiber düğüme referans içerecek veya null olacaktır. null durumunda React, iş döngüsünden çıkar ve değişiklikleri işlemeye (commit) hazır olur.

Ağaçta gezinmek ve işi başlatmak veya tamamlamak için kullanılan dört ana fonksiyon vardır:

İlk iki fonksiyon olan performUnitOfWork ve beginWork ile başlayalım:

1function performUnitOfWork(workInProgress) {
2 let next = beginWork(workInProgress);
3 if (next === null) {
4 next = completeUnitOfWork(workInProgress);
5 }
6 return next;
7}
8
9function beginWork(workInProgress) {
10 console.log('work performed for ' + workInProgress.name);
11 return workInProgress.child;
12}

performUnitOfWork fonksiyonu, workInProgress ağacından bir fiber düğüm alır ve beginWork fonksiyonu çağırarak çalışmaya başlar. Bu, bir fiber için yapılması gereken tüm aktiviteleri başlayacak olan fonksiyondur. Bu yazının amaçları doğrultusunda, sadece işin yapıldığını belirtmek için fiberin adını console'a basıyoruz. beginWork fonksiyonu, döngüde işlemek için her zaman bir sonraki çocuğa referans veya null döndürür. Bir sonraki çocuk varsa, workLoop için nextUnitOfWork değişkenine atanacaktır. Ancak çocuk yoksa React, dalın sonuna geldiğini bilir ve böylece geçerli düğümü tamamlayabilir. Düğüm tamamlandığında, kardeşler için çalışması ve bundan sonra ebeveyne geri dönmesi gerekir. Bu, completeUnitOfWork fonksiyonunda yapılır:

1function completeUnitOfWork(workInProgress) {
2 while (true) {
3 let returnFiber = workInProgress.return;
4 let siblingFiber = workInProgress.sibling;
5
6 nextUnitOfWork = completeWork(workInProgress);
7
8 if (siblingFiber !== null) {
9 // Kardeş varsa, performUnitOfWork içinde çalıştırılması için döndür.
10 return siblingFiber;
11 } else if (returnFiber !== null) {
12 // returnFiber'de başka iş yoksa, üst düğümü tamamlamak için döngüye devam et.
13 workInProgress = returnFiber;
14 continue;
15 } else {
16 // Kök'e ulaştık :)
17 return null;
18 }
19 }
20}
21
22function completeWork(workInProgress) {
23 console.log('work completed for ' + workInProgress.name);
24 return null;
25}

Fonksiyonun özünün büyük bir while döngüsü olduğunu görebilirsiniz. React, workInProgress düğümüne ait bir çocuk olmadığında bu fonksiyona girer. Mevcut fiber için çalışmayı tamamladıktan sonra, bir kardeş olup olmadığını kontrol eder. Eğer kardeş bulunursa, React fonksiyondan çıkar ve o kardeşe ait referansı döndürür. nextUnitOfWork değişkenine atanacak ve React bu kardeşten başlayarak dal için çalışacaktır. Bu noktada React'ın sadece önceki kardeşler için çalışmayı tamamladığını anlamak önemlidir. Yalnızca alt düğümlerle başlayan tüm dallar tamamlandıktan sonra üst düğüm ve gerisi için işi tamamlanır.

Uygulamadan da görebileceğiniz gibi, hem performUnitOfWork hem de completeUnitOfWork çoğunlukla iterasyon amacıyla kullanılırken, ana faaliyetler beginWork ve completeWork fonksiyonlarında gerçekleşir.

Commit aşaması

Aşama, completeRoot fonksiyonu ile başlar. Burada React, DOM'u günceller ve mutasyon öncesi ve sonrası yaşam döngüsü yöntemlerini çağırır.

Kullandığım mutasyon kelimesi genel olarak, bir React elemanının geçirdiği/geçireceği değişimi ifade eder.

React bu aşamaya geldiğinde elinde iki ağaç ve etki listesi vardır. İlk ağaç, ekranda görüntülenmekte olan durumu gösterir. İkinci ağaç, render aşamasında oluşturulmuş olan alternatif ağaçtır. Buna kaynak kodda finishedWork veya workInProgress adı verilir ve ekrana yansıtılması gereken durumu gösterir. Bu alternatif ağaç, çocuk ve kardeş referansları aracılığıyla mevcut ağaca benzer şekilde bağlanır.

Etki listesi ise, finishedWork ağacının nextEffect işaretçisi ile bağlı olan fiberlerinden oluşan bir alt kümedir. Etki listesinin, render aşamasının bir sonucu olduğunu unutmayın. render aşamasının tüm amacı, hangi düğümlerin eklenmesi, güncellenmesi veya kaldırılması gerektiğini ve hangi bileşenlerin yaşam döngüsü yöntemlerinin çağrılması gerektiğini belirlemekti. İşte etki listesi bize bunu söylüyor. Ve ayrıca, bu tam olarak commit aşamasında gezilen düğümler kümesidir.

commit aşamasında çalışan ana fonksiyon, commitRoot'tur. Temel olarak, aşağıdakileri yapar:

  • Snapshot etkisiyle etiketlenmiş düğümlerde getSnapshotBeforeUpdate yaşam döngüsü metodunu çağırır.
  • Deletion etkisiyle ile etiketlenmiş düğümlerde componentWillUnmount yaşam döngüsü metodunu çağırır.
  • Tüm DOM ekleme, güncelleme ve kaldırma işlemlerini gerçekleştirir.
  • finishedWork ağacını current (şimdiki) ile değiştirir.
  • Placement etkisiyle etiketlenmiş düğümlerde componentDidMount yaşam döngüsü metodunu çağırır.
  • Update etkisiyle ile etiketlenmiş düğümlerde componentDidUpdate yaşam döngüsü metodunu çağırır.

getSnapshotBeforeUpdate ön mutasyon metodunu çağırdıktan sonra React, bir ağaçtaki tüm yan etkileri işler (commit). Bunu iki adımda yapar. İlk adımda, tüm DOM (host) ekleme, güncelleme, silme işlemlerini ve ref bağlantılarını kaldırma işlemlerini gerçekleştirir. Daha sonra React, finishedWork ağacını, (workInProgress ağacını current ağaç olarak işaretleyerek) FiberRoot'a atar. Bu, commit aşamasının ilk adımından sonra yapılır, böylece bir önceki ağaç componentWillUnmount sırasında hala geçerli olur, ancak ikinci adımdan önce tamamlanır, böylece biten çalışma componentDidMount/Update sırasında geçerli olur. İkinci adımda React, diğer tüm yaşam döngüsü metodlarını ve ref callbacklerini çağırır. Bu metodlar ayrı bir adım olarak yürütülür, böylece ağaçtaki tüm yerleşimler, güncellemeler ve silme işlemleri zaten çağırılmış olur.

Yukarıda açıklanan adımları çalıştıran fonksiyonun özeti:

1function commitRoot(root, finishedWork) {
2 commitBeforeMutationLifecycles()
3 commitAllHostEffects();
4 root.current = finishedWork;
5 commitAllLifeCycles();
6}

Bu alt fonksiyonlardan her biri, etkiler listesinde gezinen ve etkilerin türüne göre çalışan bir döngü uygular. Fonksiyonun amacına ilişkin bir etki bulduğunda, uygular.

Mutasyon öncesi yaşam döngüsü metodları

Örneğin, bir etki ağacı üzerinde gezinen ve bir düğümün Snapshot etkisine sahip olup olmadığını kontrol eden kod:

1function commitBeforeMutationLifecycles() {
2 while (nextEffect !== null) {
3 const effectTag = nextEffect.effectTag;
4 if (effectTag & Snapshot) {
5 const current = nextEffect.alternate;
6 commitBeforeMutationLifeCycles(current, nextEffect);
7 }
8 nextEffect = nextEffect.nextEffect;
9 }
10}

Bir sınıf bileşeni için bu etki getSnapshotBeforeUpdate yaşam döngüsü metodunu çağırmak anlamına gelir.

DOM güncellemeleri

commitAllHostEffects, React'in DOM güncellemelerini gerçekleştirdiği fonksiyondur. Fonksiyon temel olarak bir düğüm için yapılması gereken işlem türünü tanımlar ve yürütür:

1function commitAllHostEffects() {
2 switch (primaryEffectTag) {
3 case Placement: {
4 commitPlacement(nextEffect);
5 ...
6 }
7 case PlacementAndUpdate: {
8 commitPlacement(nextEffect);
9 commitWork(current, nextEffect);
10 ...
11 }
12 case Update: {
13 commitWork(current, nextEffect);
14 ...
15 }
16 case Deletion: {
17 commitDeletion(nextEffect);
18 ...
19 }
20 }
21}

React'in, commitDeletion fonksiyonundaki silme işleminin bir parçası olarak componentWillUnmount metodunu çağırması ilginçtir.

Mutasyon sonrası yaşam döngüsü metodları

commitAllLifecycles, React'ın kalan tüm yaşam döngüsü yöntemlerini (componentDidUpdate ve componentDidMount) çağırdığı fonksiyondur.

Kapanış

Buraya kadar geldiğiniz için teşekkürler. Umarım keyifli bir okuma olmuştur. Eğer ingilizceniz el veriyorsa, neredeyse bire bir gittiğim Inside Fiber: an in-depth overview of the new reconciliation algorithm in React yazısını da okumanızı öneririm. Kendinize iyi bakın.

Kaynaklar