Remix: React’in Yin’ine Yang

Yılmaz Çite
7 min readSep 6, 2022

--

Bu yazı, Kent C. Dodds’ın “Remix: The Yang to React’s Yin” adlı yazısının çevirisidir. Asterisk (*) ile işaretlenmiş kelime ve terimlerin açıklamaları çeviri notu olarak o paragrafın altında bulunuyor. Keyifli okumalar.

2015’ten bu yana React ile uygulamalar geliştiriyorum. O zamandan beri React, geliştirme sürecindeki üretkenliğimi açık ara en çok artıran yegâne etken oldu. React’in, state’e dayalı kullanıcı arayüzü render ettiği declarative modeli, internet için kullanıcı arayüzü oluşturmakla ilgili düşünce şeklimi büyük ölçüde basitleştirdi. Aynı zamanda, bana state’e dair harika bir düşünce şekli verdi ve bu düşünce şekli, Angular.js ve ondan önce de Backbone kullanırkenki düşünce şeklimin çok daha ötesindeydi.

React’in sloganı şöyle:

Kullanıcı arayüzü geliştirmek için bir JavaScript kütüphanesi

React, öncüsü olduğu bu declarative bileşen modeli -declarative component model- ile bu anlamda harika bir iş çıkarıyor. State’leri yönetmeden bir kullanıcı arayüzü geliştiremezsiniz (seçim kutusu menüsü `açık``kapalı` mı?). İşte bu yüzden React’in bir bileşen state yönetim -component state management- sistemi var.

Asıl olay, web uygulamalarının yerel bileşen state’lerinden çok daha fazlası olmasıdır. Aslında, tipik bir React uygulamasında yüklenen “state”in büyük çoğunluğu state bile değildir; sunucudan gelen bir state önbelleğidir -state cache- (ki bunun veri tabanı gibi bir persistence katmanından gelmesi muhtemel). React bize state yönetimi için her zaman güzel bir yöntem vermiş olsa da, yönettiğimiz state’in çoğunun aslında önbellek olduğu ve önbelleğe alma problemleri yaşadığı gerçeğini saklayamaz.

Phil Karlton’ın, meşhur sözünde de belirttiği gibi:

Bilgisayar biliminde yalnızca iki zor şey vardır: önbellek temizleme ve adlandırma.

Birçok yönden bu bir şaka olsa da önbellek temizleme kesinlikle çetrefilli bir sorundur. Ve bu noktaya kadar, React bize bu sorunu orijinal haliyle yönetebilmemiz adına hiçbir şey sunmadı. React kullanırken bu sorunu çözmemizi kolaylaştıracak sayısız kütüphane ve aracın geliştirilmiş olması da bunun bir kanıtı. Redux (toolkit), MobX, Apollo, React Query, SWR gibi araçlardan hangisini kullanıyor olursanız olun, aslında bu araçları React’in kendisinin bir çözümünün olmadığı, ortak bir web geliştirme sorunu için kullanıyorsunuz:

Network Chasm* Yönetimi

Peki network chasm nedir?

Network chasm diyerek şunu kastediyorum:

*Network chasm: “İstemci tarafındaki state ile sunucu tarafındaki state arasında oluşan boşluk” olarak tanımlanıyor. Bu noktada network chasm, ağda oluşan boşluk/uçurum gibi bir benzetme olarak kullanılıyor.

Web geliştiriciler olarak, hem istemci (tarayıcı) tarafında hem de sunucu tarafında çalışan kodlar yazabiliyoruz. Ağ üzerinde herhangi bir kontrolümüz yok. Bu yüzden önbelleğe alma konusunu en baştan düşünmeliyiz. React bileşenimiz, kullanıcı taco seçimini 🌮 yaptıktan sonra yeniden render edildiğinde, o spesifik taco için mevcut olan seçeneklere senkronize şekilde erişim sağlamalıyız 🤤. Bu yüzden ağ üzerinden HTTP istekleri göndeririz ve gelen veriyi, React state (ya da bir kütüphane) aracılığıyla bir in-memory önbellek* içinde depolarız ki böylelikle yeniden render edilmeye hazır bulunsun.

*In-memory önbellek: “…uygulama ile ilgili verilerin uygulamanın çalıştığı uygulama sunucusunun ram belleğinde tutulması işlemidir.” Caching Nedir?

Hem büyük hem de küçük uygulamalardaki hataların bir numaralı sebebini biliyor musunuz?

Kod.

Bu network chasm, muazzam miktarda kodun kaynağıdır. Doğru yapmak son derece zor ancak biz web uygulamaları geliştiriyoruz ve bu yüzden denememiz gerekiyor. Bu yüzden, backend ile veri alışverişi yapabilmek için, JavaScript’in, modern fetch API’ın ve bazı kullanışlı kütüphanelerin gücünü birleştirerek HTTP aracılığıyla network chasm’in üzerinden bir tırmanma kancası fırlatıyoruz:

Bu tırmanma kancası fırlatma işleminin çalışması için gereken kodun tamamı frontend’de mevcut. Veri isteği göndermek -fetching- için, isteği hangi veriye göndermeniz gerektiğini bilmelisiniz. Ve çoğu zaman bu durum zorlayıcı bir sorun haline gelebilir çünkü veri isteği gönderme işlemimizi, veriye ihtiyaç duyan kod ile birlikte aynı yerde tutmayı seviyoruz (böyle yapmak, hataları ve aşırı veri isteği gönderme gibi sorunları ciddi ölçüde azaltır.) Fakat bunun da, bileşenler render edilene kadar veri isteği gönderememek gibi talihsiz bir yan etkisi vardır.

Buna, uygulamamızın daha hızlı yüklenmesi adına kodları ayrıştırma işlemini uygulama arzusunu da ekleyelim. Ve şimdi, sadece bileşeninizin render edilmesini beklemeniz gerekmesinin yanında, render işlemi başladığında, istek gönderme işlemini gerçekleştirecek koda da istek göndermeniz gerekecek. Bu da ağ üzerinde bir şelale oluşmasına -network waterfalls*- yol açar (ve hepimiz şelalelerin tehlikesini biliyoruz).

*Waterfall: Şelale benzetmesi React dokümantasyonunda “Paralel ilerlemesi gereken eylemlerde oluşan kasıtsız bir sekans” olarak belirtiliyor; “Şelaleler, render esnasında veri isteği gönderen kodlarda yaygındır.”

Ne yazık ki veri isteği gönderme tek başına bu sorunu çözemiyor. Aslında, veri isteği göndermek için React Suspense kullanmak bile bu sorunu çözmeye yetmeyecek. Suspense veriyi bileşenlerin içinden alabilmek adına birçok veri isteği gönderme kütüphanesinin yerine geçecek (ve eğer henüz önbelleğe alınmamışsa, veriyi istek gönderilmesi için tetikleyecek, ki bu çok havalı). Fakat şelale etkisinden kaçınmak istiyorsanız, veriye, o bileşenlere ait kodlar render edilmeden önce istek göndermeye başlamalısınız.

Daha Erken İstek Göndermek -Fetching-

İşte bu yüzden React Router, Remix hakkında sevdiğim çoğu şeyi kendisine entegre ederek bu sorunu çözeceği için çok heyecanlıyım. Ryan bu durumu Remixing React Router yazısında açıklıyor. İç içe yerleştirilmiş layout route’larının gücünü, `yükleyicilerin` -loaders- verileri alma ve `eylemlerin` -actions- de verileri değiştirme güçleriyle birleştirerek, verilere istek gönderme işlemini bileşenlerden ayırabilir ve yine de kodları ortak bir yerde tutma avantajından fazlasıyla faydalanabilirsiniz. Bu durumda istek gönderme kodu bileşenin içinde olmayabilir fakat iç içe yerleştirilmiş layout route’larının doğası gereği, oraya çok yakındır.

Bu özellikler sayesinde, “Veri gereksinimlerini bilmek için render etmeliyim.” noktasından “Veri gereksinimlerini URL’den biliyorum.” noktasına geçiş yapıyoruz.

Bunun üzerine, React Router şimdi bu network chasm’in bir kısmını sizin için yönetiyor ve bu da yükleme/hata state’leri ile uğraşması gereken çok daha az kodunuz olacağı anlamına geliyor. Ayrıca, React Router’ın sizin için önbellek yeniden doğrulamasını halledebileceği anlamına da geliyor! E tabii yeniden gönderilen formlar ve race condition’ları* da (bunlar kullanıcı arayüzü geliştirirken karşılaşılan zorlu sorunlardan bazıları). Harika bir kullanıcı deneyimi (optimistic UI* modelleri gibi) oluşturmak hiç bu kadar kolay olmamıştı. Aşağıdaki görsel, network chasm’i sizin için etkili bir şekilde daraltıyor:

*Race condition: “Race Condition birden çok thread’in paylaşılan bir hafıza alanına aynı anda ulaşıp o alanı değiştirmesiyle meydana gelir.” Race Condition nedir ve nasıl önlenir?

*Optimistic UI: “Kullanıcı deneyimini iyileştirmek için tasarıma ve uygulamaya/webe dahil edilen bir yöntem.” Daha hızlı kullanıcı deneyimi: Optimistic UI

Daha iyisini yapabilir miyiz?

Bu özelliklere React Router’da sahip olmak, kodunu basitleştirmek ve uygulamasının performansını artırmak isteyen herkes için harika bir avantaj olacak. React Router, veri isteği göndermek için React Suspense kullanan herkesin en iyi arkadaşı olacak (tabii sanırım eğer Meta’nın sahip olduğu altyapı, derleyici ve yönlendiriciye sahip değilseniz).

Ama daha iyisini bile yapabiliriz. Tarayıcıdan veri isteği göndermeye daha erken başlasanız bile, yine de kullanıcılarınızın herhangi bir şey görmeden önce ilk JavaScript paketlerinin görüntülenip yürütülmesini beklemeleri gerekiyor. React Router’ın, verilerin yüklenmesi ve değiştirilmesini yönetme konularındaki yardımıyla, birçok state (önbellek) yönetim kodunu silebilirsiniz ama yine de hâlâ hepsi tarayıcıda bulunur. Bunun da ötesinde, kodun istek göndermesine, istek sonucu alınan verilere gereksinim duyan kodu almadan önce ihtiyacımız olduğu için, bu, kodların artık ayrıştırılmış olmadığı anlamına gelir.

Bütün o kodları tarayıcıdan alıp bir sunucuya taşıyabilsek harika olmaz mıydı? Ne zaman bir veri tabanı ile iletişim kurmanız gerekse sunucusuz -serverless- fonksiyonlar yazmak zorunda olmak veya özel anahtar talep eden bir API’a başvurmak sinir bozucu değil mi? (Evet öyle). Bunlar, React Sunucu Bileşenleri’nin -React Server Components- bizim için yapma sözü verdiği türden şeylerdir. Veri yükleme -data loading- konusunda onlara kesinlikle güvenebiliriz fakat veri değişimi -data mutations- konusunda hiçbir şey yapmıyorlar ve bu kodu da tarayıcının dışına taşıyabilmek (ve yüklenmesini beklemek zorunda olmamak) güzel olurdu.

Ve sağdan sahneye girer: Remix 💿

Uygulamanızı bir üst seviyeye gerçekten taşımak için, uygulamanızın sunucu tarafında render edilmesini isteyeceksiniz. Ve bunu yapmanın en iyi yolu ise Remix kullanmak. Remix, ağ sınırı -network boundary- üzerindeki köprüyü sizin için öyle şekilde tamamlıyor ki bu konu üzerine düşünmeniz bile gerekmiyor. Bütün veri isteği gönderme ve veri değişimi kodlarınızı alıp, alışılagelmiş -conventional- “Remix route modülleri”nden export edilen fonksiyonlar olmaları için taşıyabilirsiniz. Böylelikle aniden tüm bu kodlar sunucuda kalır ve network chasm’in tamamı, Remix tarafından sizin için idare edilir:

İşte şimdi uygulamanız gerçekten uçabilir ⚡ çünkü kullanıcı artık JavaScript’in yüklenmesini beklemek zorunda kalmayacak. Uygulama karşılarında, kullanıma hazır durumda olacak (ve JavaScript arka planda indiriliyorken, aşamalı geliştirme -progressive enhancement- sayesinde tüm bağlantılar ve formlar da çalışıyor olacak).

Ve şunu dinleyin, artık sunucu tarafında çalışan kodlar yazabildiğiniz için doğrudan veri tabanı ile iletişime geçme veya özel anahtarlar kullanarak API’lara başvurma konularında endişelenmeniz gerekmiyor. Yükleyici ve eylemleriniz yalnızca sunucu üzerinde çalışır ve böylelikle ne yapmalarını isterseniz onu yaparlar. Güzel bir DX iyileştirmesi!

Uygulamanızın tamamı

Remix’in size sunucunun gücünü vermesi, eğer ihtiyacınız olursa uygulamanızın tamamını idare edebileceği anlamına geliyor. Herkes işleri bu şekilde yapmak istemez fakat veri tabanlarıyla ve üçüncü parti servislerle doğrudan iletişim kurabilen bir backend’iniz olduğu için uygulamanızın yapısının daha çok şu şekilde görünmesini sağlayabilirsiniz:

Harika olan şey şu ki, Remix kullanarak, bütünüyle yönetilen bir network chasm’in bütün avantajlarından faydalanırsınız. Bu noktada tamamen Remix ile çalışıp çalışmadığınız da önemli değil. Yani eğer mevcut backend’inizden memnunsanız, kesinlikle kullanmaya devam edin.

Sonuç

React’in sloganı şöyle:

Kullanıcı arayüzü geliştirmek için bir JavaScript kütüphanesi

Ve bunda müthiş bir iş çıkarıyor. React hiçbir zaman “network chasm yönetimi” vaadinde bulunmadı fakat her web uygulamasının buna ihtiyacı var. Network chasm’i yöneten Remix ile, nihayet React’in yin’ine bir yang’ımız var. Harika bir renderlama kütüphanesi ve mükemmel bir network chasm yöneticisi ile daha iyi ve daha hızlı uygulamaları daha az hata ve daha basit kod ile daha çok eğlenerek geliştirebilirsiniz.

Kişisel bir not olarak, Remix ile web uygulamaları geliştirmeye aşık olmamı sağlayan şey bu oldu. Bugün bu yazıyı okuduğunuz internet sitesi, Remix ile yeniden yazılmış olmasının bir sonucudur. Başlarken ne kadar havalı şeyler çıkacağından bihaberdim. Bunların hepsini Remix sağladı çünkü temel özellikleri bitirdiğimde, çok daha fazlasını yapmaya vaktim ve kapasitem olduğunu fark ettim (internet sitemi yeniden kodlamam üzerine daha fazlası). Remix, aklımdaki eğlenceli fikirlere “evet” diyebileceğimi hissettirdi ve bu çok canlandırıcı bir histi.

Umarım bu, daha iyi internet siteleri geliştirme arayışınızda size yardımcı olur. Esen kalın. 😎

--

--