Recoil, atom modelini React dünyasına tanıttı. Yeni güçleri, dik bir öğrenme eğrisi ve seyrek öğrenme kaynakları pahasına geldi.
Jotai ve Zedux daha sonra bu yeni modelin çeşitli yönlerini basitleştirerek birçok yeni özellik sundu ve bu şaşırtıcı yeni paradigmanın sınırlarını zorladı.
Diğer makaleler bu araçlar arasındaki farklara odaklanacaktır. Bu makale, her üçünün de ortak olduğu büyük bir özelliğe odaklanacaktır:
Flux'u düzelttiler.
Flux'u bilmiyorsanız işte kısa bir özet:
Redux'un yanı sıra tüm Flux tabanlı kütüphaneler temel olarak şu modeli izledi: Bir uygulamanın birden fazla mağazası vardır. Görevi tüm mağazalara eylemleri doğru sırayla iletmek olan tek bir Sevk Görevlisi vardır. Bu "doğru sıralama", mağazalar arasındaki bağımlılıkların dinamik olarak çözülmesi anlamına gelir.
Örneğin, bir e-ticaret uygulaması kurulumunu ele alalım:
Kullanıcı, örneğin sepetine bir muz taşıdığında, PromosStore'un, kullanılabilir bir muz kuponu olup olmadığını görmek için bir istek göndermeden önce CartStore'un durumunun güncellenmesini beklemesi gerekir.
Veya muzlar kullanıcının bulunduğu bölgeye gönderilemiyor olabilir. CartStore'un güncellemeden önce UserStore'u kontrol etmesi gerekiyor. Ya da belki kuponlar yalnızca haftada bir kez kullanılabilir. PromosStore'un kupon isteğini göndermeden önce UserStore'u kontrol etmesi gerekir.
Flux bu bağımlılıklardan hoşlanmaz. Eski React belgelerinden :
Flux uygulamasındaki nesneler oldukça ayrıştırılmıştır ve bir sistem içindeki her nesnenin sistemdeki diğer nesneler hakkında mümkün olduğunca az bilgi sahibi olması gerektiği ilkesi olan Demeter Yasasına çok güçlü bir şekilde bağlıdır.
Bunun arkasındaki teori sağlamdır. 100%. Peki... Flux'un bu çok mağazalı lezzeti neden öldü?
Yalıtılmış durum konteynerleri arasındaki bağımlılıkların kaçınılmaz olduğu ortaya çıktı. Aslında kodu modüler ve DRY tutmak için diğer mağazaları sık sık kullanıyor olmalısınız .
Flux'ta bu bağımlılıklar anında oluşturulur:
// This example uses Facebook's own `flux` library PromosStore.dispatchToken = dispatcher.register(payload => { if (payload.actionType === 'add-to-cart') { // wait for CartStore to update first: dispatcher.waitFor([CartStore.dispatchToken]) // now send the request sendPromosRequest(UserStore.userId, CartStore.items).then(promos => { dispatcher.dispatch({ actionType: 'promos-fetched', promos }) }) } if (payload.actionType === 'promos-fetched') { PromosStore.setPromos(payload.promos) } }) CartStore.dispatchToken = dispatcher.register(payload => { if (payload.actionType === 'add-to-cart') { // wait for UserStore to update first: dispatcher.waitFor([UserStore.dispatchToken]) if (UserStore.canBuy(payload.item)) { CartStore.addItem(payload.item) } } })
Bu örnek, bağımlılıkların mağazalar arasında nasıl doğrudan bildirilmediğini, bunun yerine eylem bazında bir araya getirildiklerini gösterir. Bu resmi olmayan bağımlılıkları bulmak için uygulama kodunun kazılması gerekir.
Bu çok basit bir örnek! Ancak Flux'un nasıl karmakarışık bir his verdiğini zaten görebilirsiniz. Yan etkiler, seçim işlemleri ve durum güncellemelerinin tümü bir araya getirilmiştir. Bu birliktelik aslında güzel olabilir. Ancak bazı gayri resmi bağımlılıkları karıştırın, tarifi üçe katlayın ve standart olarak servis edin; Flux'un hızla bozulduğunu göreceksiniz.
Flummox ve Reflux gibi diğer Flux uygulamaları ortak metin ve hata ayıklama deneyimini geliştirdi. Oldukça kullanışlı olmasına rağmen bağımlılık yönetimi tüm Flux uygulamalarını rahatsız eden tek sorundu. Başka bir mağazayı kullanmak çirkin hissettirdi. Derinlemesine yuvalanmış bağımlılık ağaçlarını takip etmek zordu.
Bu e-ticaret uygulamasının bir gün OrderHistory, ShippingCalculator, DeliveryEstimate, BananasHoarded vb. için mağazaları olabilir. Büyük bir uygulamanın kolayca yüzlerce mağazası olabilir. Bağımlılıkları her mağazada nasıl güncel tutuyorsunuz? Yan etkileri nasıl takip ediyorsunuz? Peki ya saflık? Hata ayıklamaya ne dersiniz? Muz gerçekten bir meyve midir?
Flux'un getirdiği programlama ilkelerine gelince, tek yönlü veri akışı kazanandı ancak şimdilik Demeter Yasası kazanamadı.
Redux'un günü kurtarmak için nasıl kükreyerek geldiğini hepimiz biliyoruz. Birden fazla mağaza konseptini tekil bir model lehine terk etti. Artık her şey, herhangi bir "bağımlılık" olmadan, diğer her şeye erişebilir.
İndirgeyiciler saftır, dolayısıyla çoklu durum dilimleriyle ilgilenen tüm mantık, deponun dışına çıkmalıdır . Topluluk, yan etkileri ve türetilmiş durumu yönetmek için standartlar oluşturdu. Redux mağazaları güzel bir şekilde hata ayıklanabilir. Redux'un başlangıçta düzeltmeyi başaramadığı tek büyük Akı Kusuru standart metindi.
RTK daha sonra Redux'un kötü şöhretli standart metnini basitleştirdi. Daha sonra Zustand, bir miktar hata ayıklama gücü pahasına bazı tüyleri kaldırdı. Bu araçların tümü React dünyasında son derece popüler hale geldi.
Modüler durumla, bağımlılık ağaçları o kadar doğal bir şekilde karmaşık bir şekilde büyür ki, düşünebildiğimiz en iyi çözüm "Sanırım yapma bunu." oldu.
Ve işe yaradı! Bu yeni singleton yaklaşımı çoğu uygulama için hala yeterince iyi çalışıyor. Flux ilkeleri o kadar sağlamdı ki bağımlılık kabusunu ortadan kaldırmak sorunu çözdü.
Yoksa yaptı mı?
Singleton yaklaşımının başarısı şu soruyu akla getiriyor: Flux ilk etapta neyi hedefliyordu? Neden birden fazla mağaza istedik?
Bu konuya biraz ışık tutmama izin verin.
Birden fazla mağazayla devletin parçaları kendi özerk, modüler konteynerlerine bölünür. Bu mağazalar ayrı ayrı test edilebilir. Ayrıca uygulamalar ve paketler arasında kolayca paylaşılabilirler.
Bu özerk depolar ayrı kod parçalarına bölünebilir. Bir tarayıcıda tembel olarak yüklenebilir ve anında takılabilirler.
Redux'un redüktörlerinin kod bölünmesi de oldukça kolaydır. replaceReducer
sayesinde tek ekstra adım, yeni birleşik redüktörü oluşturmaktır. Ancak yan etkiler ve ara yazılımlar söz konusu olduğunda daha fazla adım gerekli olabilir.
Singleton modeliyle, harici bir modülün dahili durumunu kendi durumunuzla nasıl entegre edeceğinizi bilmek zordur. Redux topluluğu bunu çözmek için Ducks modelini tanıttı. Ve küçük bir kalıp pahasına işe yarıyor.
Birden fazla mağaza söz konusu olduğunda, harici bir modül bir mağazayı kolayca ortaya çıkarabilir. Örneğin, bir form kitaplığı bir FormStore'u dışa aktarabilir. Bunun avantajı standardın "resmi" olmasıdır, yani insanların kendi metodolojilerini oluşturma olasılıkları daha düşüktür. Bu, daha sağlam, birleşik bir topluluk ve paket ekosistemine yol açar.
Singleton modeli şaşırtıcı derecede performanslıdır. Redux bunu kanıtladı. Ancak seçim modelinin özellikle katı bir üst sınırı var. Bu Yeniden Seçim tartışmasında bununla ilgili bazı düşünceler yazdım. Büyük, pahalı bir seçici ağaç, önbellekleme üzerinde maksimum kontrole sahip olsanız bile gerçekten sürüklenmeye başlayabilir.
Öte yandan, birden fazla mağaza söz konusu olduğunda çoğu durum güncellemesi, durum ağacının küçük bir bölümünde izole edilir. Sistemdeki başka hiçbir şeye dokunmuyorlar. Bu, tekil yaklaşımın çok ötesinde ölçeklenebilir; aslında birden fazla depoyla, kullanıcının makinesindeki bellek sınırlamalarına çarpmadan önce CPU sınırlamalarına ulaşmak çok zordur.
Redux'da durumu yok etmek çok zor değil. Tıpkı kod bölme örneğinde olduğu gibi, indirgeyici hiyerarşisinin bir bölümünü kaldırmak için yalnızca birkaç ekstra adım gerekir. Ancak birden fazla mağazayla bu hala daha basittir; teorik olarak mağazayı sevk görevlisinden ayırabilir ve çöplerin toplanmasına izin verebilirsiniz.
Bu, Redux, Zustand ve singleton modelinin genel olarak iyi idare edemediği büyük sorundur. Yan etkiler etkileşime girdiği durumdan ayrılır. Seçim mantığı her şeyden ayrılmıştır. Çok mağazalı Flux belki de çok fazla aynı yerde bulunurken, Redux tam tersi bir uç noktaya gitti.
Birden fazla özerk mağaza ile bu şeyler doğal olarak birlikte gider. Aslında Flux'ta her şeyin karmakarışık bir saçma sapan karmaşaya dönüşmesini engelleyecek birkaç standart yoktu (kusura bakmayın).
Şimdi, eğer OG Flux kütüphanesini biliyorsanız aslında bunların hiç de harika olmadığını biliyorsunuzdur. Sevk görevlisi hâlâ küresel bir yaklaşım benimsiyor; her eylemi her mağazaya gönderiyor. Gayri resmi/örtük bağımlılıklarla ilgili her şey aynı zamanda kod bölmeyi ve yok etmeyi mükemmel olmaktan çıkardı.
Yine de Flux'un pek çok harika özelliği vardı. Ayrıca, çoklu mağaza yaklaşımı, Kontrolün Tersine çevrilmesi ve fraktal (yerel olarak da bilinir) durum yönetimi gibi daha fazla özellik için potansiyele sahiptir.
Birisi tanrıçasına Demeter adını vermeseydi Flux gerçekten güçlü bir devlet yöneticisine dönüşebilirdi. Ben ciddiyim! ... Tamam değilim. Ama şimdi siz bahsettiğinize göre, belki Demeter yasası daha yakından bakmayı hak ediyor:
Bu sözde "yasa" tam olarak nedir? Vikipedi'den :
- Her birimin diğer birimler hakkında yalnızca sınırlı bilgiye sahip olması gerekir: yalnızca mevcut birimle "yakından" ilişkili birimler.
- Her birim yalnızca arkadaşlarıyla konuşmalıdır; yabancılarla konuşma.
Bu yasa, Nesneye Dayalı Programlama göz önünde bulundurularak tasarlanmıştır ancak React durum yönetimi de dahil olmak üzere birçok alanda uygulanabilir.
Temel fikir bir mağazanın şunları yapmasını önlemektir:
Muz açısından bakıldığında, bir muz başka bir muzu soymamalı ve başka bir ağaçtaki muzla konuşmamalıdır. Ancak iki ağaç önce bir muz telefon hattı kurarsa diğer ağaçla konuşabilir .
Bu, endişelerin ayrılmasını teşvik eder ve kodunuzun modüler, KURU ve KATI kalmasına yardımcı olur. Sağlam teori! Peki Flux'ta eksik olan neydi?
Mağazalar arası bağımlılıklar iyi, modüler bir sistemin doğal bir parçasıdır. Bir mağazanın başka bir bağımlılık eklemesi gerekiyorsa bunu yapmalı ve mümkün olduğunca açık bir şekilde yapmalıdır. İşte yine Flux kodunun bir kısmı:
PromosStore.dispatchToken = dispatcher.register(payload => { if (payload.actionType === 'add-to-cart') { // wait for CartStore to update first: dispatcher.waitFor([CartStore.dispatchToken]) // now send the request sendPromosRequest(UserStore.userId, CartStore.items).then(promos => { dispatcher.dispatch({ actionType: 'promos-fetched', promos }) }) } if (payload.actionType === 'promos-fetched') { PromosStore.setPromos(payload.promos) } })
PromosStore'un farklı şekillerde bildirilen birden fazla bağımlılığı vardır; CartStore
bekler ve buradan okur ve UserStore
okur. Bu bağımlılıkları keşfetmenin tek yolu PromosStore'un uygulamasındaki mağazaları aramaktır.
Geliştirme araçları da bu bağımlılıkların daha keşfedilebilir olmasına yardımcı olamaz. Başka bir deyişle, bağımlılıklar çok örtülüdür.
Bu çok basit ve yapmacık bir örnek olsa da Flux'un Demeter Yasasını nasıl yanlış yorumladığını gösteriyor. Bunun çoğunlukla Flux uygulamalarını küçük tutma arzusundan doğduğuna eminim (gerçek bağımlılık yönetimi karmaşık bir iştir!), Flux'un yetersiz kaldığı nokta burasıdır.
Bu hikayenin kahramanlarının aksine:
2020'de Recoil sahneye çıktı. İlk başta biraz hantal olsa da bize Flux'un çoklu mağaza yaklaşımını yeniden canlandıran yeni bir model öğretti.
Tek yönlü veri akışı mağazanın kendisinden bağımlılık grafiğine taşındı. Mağazalara artık atom deniyordu. Atomlar düzgün bir şekilde özerkti ve kodlara bölünebilirdi. Gerilim desteği ve hidrasyon gibi yeni güçlere sahiplerdi. Ve en önemlisi, atomlar resmen bağımlılıklarını ilan ediyorlar.
Atom modeli doğdu.
// a Recoil atom const greetingAtom = atom({ key: 'greeting', default: 'Hello, World!', })
Recoil, şişirilmiş kod tabanı, bellek sızıntıları, kötü performans, yavaş geliştirme ve kararsız özelliklerle (en önemlisi yan etkilerle) mücadele ediyordu. Bunlardan bazılarını yavaş yavaş çözecekti ama bu arada diğer kütüphaneler Recoil'in fikirlerini alıp onlarla birlikte hareket etmeye başladı.
Jotai sahneye çıktı ve hızla takipçi kazandı.
// a Jotai atom const greetingAtom = atom('Hello, World!')
Recoil'in boyutunun küçük bir kısmı olmasının yanı sıra Jotai, WeakMap tabanlı yaklaşımı sayesinde daha iyi performans, daha şık API'ler ve hiçbir bellek sızıntısı sunmuyordu.
Ancak bu, bir miktar güce mal oldu; WeakMap yaklaşımı, önbellek kontrolünü zorlaştırıyor ve birden fazla pencere veya diğer alanlar arasında durum paylaşımını neredeyse imkansız hale getiriyor. Ve dize tuşlarının olmaması, şık olmasına rağmen hata ayıklamayı bir kabusa dönüştürüyor. Çoğu uygulamanın bunları tekrar eklemesi gerekir, bu da Jotai'nin şıklığını büyük ölçüde zedeler.
// a (better?) Jotai atom const greetingAtom = atom('Hello, World!') greetingAtom.debugLabel = 'greeting'
Mansiyona layık görülen birkaç kişi Reatom ve Nanostores'tur . Bu kütüphaneler atom modelinin arkasındaki teoriyi daha fazla araştırdı ve boyutunu ve hızını sınırlara kadar zorlamaya çalıştı.
Atom modeli hızlıdır ve çok iyi ölçeklenir. Ancak çok yakın zamana kadar hiçbir atom kütüphanesinin tam olarak ele almadığı birkaç endişe vardı:
Öğrenme eğrisi. Atomlar farklıdır . Bu kavramları React geliştiricileri için nasıl ulaşılabilir hale getirebiliriz?
Dev X ve hata ayıklama. Atomları nasıl keşfedilebilir hale getiririz? Güncellemeleri nasıl takip edersiniz veya iyi uygulamaları nasıl uygularsınız?
Mevcut kod tabanları için artımlı geçiş. Harici mağazalara nasıl erişirsiniz? Mevcut mantığı nasıl sağlam tutuyorsunuz? Tamamen yeniden yazmaktan nasıl kaçınırsınız?
Eklentiler. Atom modelini nasıl genişletilebilir hale getirebiliriz? Olası her durumun üstesinden gelebilir mi ?
Bağımlılık Enjeksiyonu. Atomlar doğal olarak bağımlılıkları tanımlar, ancak test sırasında veya farklı ortamlarda bunlar değiştirilebilir mi?
Demeter Yasası. Uygulama ayrıntılarını nasıl gizleyebiliriz ve dağınık güncellemeleri nasıl önleyebiliriz?
İşte burada ben devreye giriyorum. Bakın ben başka bir atom kütüphanesinin baş yaratıcısıyım:
Zedux nihayet birkaç hafta önce sahneye çıktı. New York'ta bir Fintech şirketi (benim de çalıştığım şirket) tarafından geliştirilen Zedux, yalnızca hızlı ve ölçeklenebilir olacak şekilde değil, aynı zamanda şık bir geliştirme ve hata ayıklama deneyimi sağlayacak şekilde tasarlandı.
// a Zedux atom const greetingAtom = atom('greeting', 'Hello, World!')
Burada Zedux'un özellikleri hakkında derinlemesine girmeyeceğim - söylediğim gibi, bu makale bu atom kütüphaneleri arasındaki farklara odaklanmayacaktır.
Zedux'un yukarıdaki tüm endişeleri giderdiğini söylemek yeterli. Örneğin, gerçek Kontrolün Ters Çevirilmesini sunan ilk atom kütüphanesidir ve uygulama ayrıntılarını gizlemek için atom aktarımı sunarak bizi Demeter Yasasına tam anlamıyla geri getiren ilk kütüphanedir.
Flux'un son ideolojileri nihayet yeniden canlandırıldı - yalnızca yeniden canlandırılmakla kalmadı, aynı zamanda geliştirildi! - atom modeli sayesinde.
Peki atom modeli tam olarak nedir ?
Bu atom kütüphanelerinin birçok farklılığı var; hatta "atomik" kelimesinin ne anlama geldiğine dair farklı tanımları bile var. Genel fikir birliği, atomların Yönlendirilmiş Döngüsel Olmayan Grafik aracılığıyla reaktif olarak güncellenen küçük, izole edilmiş, özerk durum kapları olduğu yönündedir.
Biliyorum, kulağa karmaşık geliyor ama muzla açıklayana kadar bekleyin.
Şaka yapıyorum! Aslında çok basit:
Güncelleştirmeler grafik üzerinden seker. Bu kadar!
Mesele şu ki, uygulama veya anlambilimden bağımsız olarak, tüm bu atomik kütüphaneler çoklu mağaza konseptini yeniden canlandırdı ve onları yalnızca kullanılabilir değil, aynı zamanda birlikte çalışmayı gerçek bir keyif haline getirdi.
Birden fazla mağaza istemek için verdiğim 6 neden, atom modelinin bu kadar güçlü olmasının nedenleridir:
Basit API'ler ve ölçeklenebilirlik tek başına atom kitaplıklarını her React uygulaması için mükemmel bir seçim haline getirir. Redux'tan daha fazla güç ve daha az standart mı? Bu bir rüya mı?
Ne yolculuk! React durum yönetimi dünyası her zaman şaşırtmaya devam ediyor ve otostop çektiğim için çok mutluyum.
Daha yeni başlıyoruz. Atomlarla ilgili yeniliklere çok yer var. Yıllarımı Zedux'u yaratıp kullanmaya harcadıktan sonra atom modelinin ne kadar güçlü olabileceğini gördüm. Aslında gücü Aşil topuğudur:
Geliştiriciler atomları keşfettiklerinde olasılıkları o kadar derinlemesine incelerler ki, "Atomların bu sorunu ne kadar basit ve zarif bir şekilde çözdüğüne bakın" yerine "Şu çılgın karmaşık güce bakın" diyerek geri gelirler. Bunu değiştirmek için buradayım.
Atom modeli ve arkasındaki teori, çoğu React geliştiricisinin anlayabileceği bir şekilde öğretilmemiştir. Bir bakıma, React dünyasının şimdiye kadarki atom deneyimi Flux'un tam tersi oldu:
Bu makale, React geliştiricilerinin atom kitaplıklarının nasıl çalıştığını ve neden bir tane kullanmak isteyebileceğinizi anlamalarına yardımcı olmak için ürettiğim bir dizi öğrenme kaynağının ikincisidir. İlk makaleye göz atın - Ölçeklenebilirlik: React State Yönetiminin Kayıp Düzeyi .
10 yıl sürdü, ancak Flux tarafından sunulan sağlam CS teorisi, atom modeli sayesinde nihayet React uygulamalarını büyük ölçüde etkiliyor. Ve önümüzdeki yıllarda da bunu yapmaya devam edecek.