TypeScript ve React Hooks (II) ile Daha İyi Bileşenler Nasıl Oluşturulur
Bu TypeScript öğreticisinin ilk bölümünde, birleşim türlerinin ne olduğunu ve tür yüklemlerini kullanarak birini diğerinden nasıl ayırt edebileceğimizi gördük. O yazıyı okumadıysanız, şimdi okumanızı tavsiye ederim, çünkü temelleri atıyor çünkü bugün neyi tartışacağımızı anlamanız gerekecek.
Bu yazıda, birleşim türlerini kullanarak “imkansız durumları imkansız kılmak” ve daha iyi bir veri modelinin nasıl daha sağlam ve güvenilir kodlara yol açtığından bahsedeceğiz. Ayrıca, Nelio Popups'ın kaynak koduna bakarak TypeScript'in daha iyi React bileşenleri yazmamıza nasıl yardımcı olabileceğini de göreceğiz.
İmkansız Halleri Nasıl İmkansız Hale Getirirsiniz?
Basit bir örnekle başlayalım. Bir gönderi listesi görüntüleyen bir PostList bileşeni oluşturmak istediğinizi hayal edin. Bileşen ilk görüntülendiğinde, sunucudan gönderilerin listesini yüklemesini ister (bunun için usePosts adlı bir efekt kancanız olduğunu varsayalım). Verileri yüklerken bir animasyon gösteriyor; gönderiler hazır olduğunda, onları gösterir. Böyle bir bileşeni aşağıdaki gibi uygulayabiliriz:
export const PostList = ( { postIds } ) => { const { isLoading, posts } = usePosts( postIds ); if ( isLoading ) return <Spinner />; if ( ! posts.length ) { return <p>No posts</p> }//end if return ( <div> { posts.map( ( post ) => ... ) } <div> ); }
Az önce paylaştığım bileşen açıklamasına ve önceki snippet'e göre, usePosts aşağıdaki dönüş türüne sahip olabileceğini çıkarabiliriz:
type PostResult = { readonly isLoading: boolean; readonly posts?: ReadonlyArray< Post >; }
diğer bir deyişle, boolean isLoading özniteliğine ve (isteğe bağlı) bir posts listesine sahip bir nesne. posts isteğe bağlı olup olmayacağını tartışabilirsiniz; Sanırım bunun nedeni, veriler yüklenirken elimizde hiç gönderi yok, değil mi?
Her neyse, önceki tip tanımını kullanarak aşağıdaki örnekler oluşturulabilir:
// Result 1 { isLoading: true } // Result 2 { isLoading: true, posts: [] } // Result 3 { isLoading: true, posts: [ {…}, {…}, … ] } // Result 4 { isLoading: false } // Result 5 { isLoading: false, posts: [] } // Result 6 { isLoading: false, posts: [ {…}, {…}, … ] }
TypeScript Playground'da görebileceğiniz gibi, bunların tümü PostResult türünün geçerli örnekleridir. Ancak, aslında hiçbir anlam ifade etmeyen birkaç sonuç var . Spesifik olarak, bir yandan sonuç 2 ve 3 ve diğer yandan sonuç 4 tuhaftır.
2. ve 3. sonuçlara bakarsak, gönderilerin hala yüklenmekte olduğunu görürüz ( isLoading özniteliği bunu gösterir) ve yine de, aynı zamanda bazı sonuçlarımız da vardır (bir posts listesi vardır). Öte yandan, sonuç 4'te, mevcut posts listesine sahip olmalıyız… ancak nitelik ayarlanmadı bile! Bu açıkça yanlış, değil mi?
Bu, zayıf bir veri modelinin açık bir örneğidir. PostResult ile imkansız durumları temsil edebiliyoruz. "Sonuçları yüklemek" ve aynı zamanda "bir gönderi listesine sahip olmak" imkansız olmalıdır. Ayrıca "sonuçların yüklendiğini bilmek" ve " posts özniteliğini kaçırmak" da imkansız olmalıdır. Yine de veri modelimizde her iki durum da mümkündür.
(İyi) programcılar olarak amacımız, iyi yazılımlar yapmaktır ve bu, "imkansız durumları imkansız hale getirmemiz" gerektiği anlamına gelir. Çalışan örneğimizde, aşağıdaki birleşim türünü kullanarak bunu başarabiliriz (bu arada, isLoading özniteliği tarafından ayırt edilir):
type PostResult = LoadingPostResult | LoadedPostResult; type LoadingPostResult = { readonly isLoading: true; }; type LoadedPostResult = { readonly isLoading: false; readonly posts: ReadonlyArray< Post >; }
Bu yeni PostResult tanımını TypeScript Playground'da uygularsak, sonuçların 2, 3 ve 4'ün gerçekten nasıl geçersiz hale geldiğini göreceğiz. Bu nedenle, artık gerçekliği mükemmel bir şekilde yakalayan bir tanımımız var ve sonuç olarak TypeScript, kısıtlamalarımızın uygun şekilde garanti edildiğinden emin olmamıza yardımcı olacak.
Konu hakkında daha fazla bilgi edinmek istiyorsanız, Richard Feldman'ın bu konuşmasını tavsiye ederim. Konuşmasında Karaağaç kullanıyor, ancak paylaştığı örnekler ve içgörüler yine de çok ilginç:
Nelio Popups Veri Modelini tanımlama (bir parçası)
Gönderinin başında size gerçek bir örnek kullanacağımıza söz verdim, bu yüzden bunu yapmamızın zamanı geldi. WordPress.org'daki Nelio Popups kaynak koduna bir göz atarsanız, src/common common'da, içinde açılır pencerelerimizin ana types tanımladığımız, içinde birkaç dosya bulunan, type adında bir klasör olduğunu göreceksiniz. Bu dosyalardan birini rastgele alırsak (örneğin, src/common/types/popups/style.ts ) ve içeriğine bakarsak, içerdiği türlerin az önce tartıştığımıza oldukça benzer olduğunu görürüz. önceki bölüm:
// … export type OverlaySettings = | { readonly isEnabled: false; } | { readonly isEnabled: true; readonly color: Color; }; // … export type BorderSettings = | { readonly isEnabled: false; } | { readonly isEnabled: true; readonly radius: CssSizeUnit; readonly color: Color; readonly width: CssSizeUnit; };
Yani, her türün bize belirli bir özelliğin etkin olup olmadığını söyleyen bir bayrağı vardır ve eğer etkinse, onu ayarlamak için ek özniteliklerimiz vardır. Peki, bu daha iyi kod yazmamıza nasıl yardımcı olur?
Tepki Bileşenleri
Gördüğünüz gibi , Nelio Pop-up'larındaki türler oldukça basittir ve basitçe şu ilkeyi uygular: "imkansızı imkansız yap". Böyle bir ilkenin gelişimimize nasıl rehberlik edebileceğini görelim.
Nelio Popups'tan çektiğim örnek türlerden biri OverlaySettings . Adından ve özelliklerinden de anlaşılacağı gibi, Nelio Popups bir bindirme içerebilir veya içermeyebilir ve içerdiğinde kullanıcı rengini tanımlayabilir. Bunun bir ayarını yöneten bir bileşen aşağıdaki gibi olacaktır:

aşağıdaki kaynak koduyla:
import * as React from '@wordpress/element'; import { ToggleControl } from '@wordpress/components'; import { _x } from '@wordpress/i18n'; import { ColorControl } from '@nelio/popups/components'; import { usePopupMeta } from '@nelio/popups/hooks'; export const OverlayControl = (): JSX.Element => { const [ overlay, setOverlay ] = usePopupMeta( 'overlay' ); const { isEnabled } = overlay; const onChange = ( isChecked: boolean ) => isChecked ? setOverlay( { color: '#000000cc', isEnabled: true } ) : setOverlay( { isEnabled: false } ); return ( <> <ToggleControl label={ _x( 'Add overlay behind popup', 'command', 'nelio-popups' ) } checked={ isEnabled } onChange={ onChange } /> { isEnabled && ( <ColorControl color={ overlay.color } onChange={ ( newColor ) => setOverlay( { ...overlay, color: newColor, } ) } /> ) } </> ); };
Önceki pasajda bahsetmeye değer birkaç şey var. Her şeyden önce, bir overlay örneği verildiğinde, güvenlik koruması veya kontrol olmadan isEnabled nasıl erişebileceğimize dikkat edin. Bunun nedeni, türünün tanımında ( OverlaySettings ) görebileceğiniz gibi, özniteliğin birleşim türünün ayırıcısı olması ve bu nedenle birleşim türünün tüm "alt türlerinde" tanımlanmış olmasıdır. Böylece güvenli bir şekilde imha edebiliriz.
İkincisi, fonksiyonda döndürdüğümüz JSX'in iki parçası var. Bir yandan bileşen, kaplama ayarını açmak veya kapatmak için her zaman bir anahtar döndürür. Yani, isEnabled özniteliğini yönetmekten sorumlu bir alt bileşenimiz var. Öte yandan, kaplamanın color yönetmek için ikinci bir alt bileşenimiz var. Bununla birlikte, bu ikinci bileşen, yalnızca ve yalnızca, bindirme isEnabled olduğunda görünür, bu da mükemmel bir anlam ifade eder, çünkü yalnızca kaplama gerçekten etkinleştirildiğinde bir color desteğine sahibiz.
Bu noktada, bunun gibi daha basit bir tür hile yapacaksa neden bir birlik türü oluşturduğumuzu merak ediyor olabilirsiniz:
type OverlaySettings = { readonly isEnabled: true; readonly color: Color; };
İyi düşünmek! Dediğim gibi, veri modelimizi tanımlarken kesin olmak uzun vadede bize yardımcı olacaktır. Öyleyse, bu kadar kısa bir örnekle, ilk türün ikincisinden nasıl daha iyi olduğuna daha yakından bakalım.
Az önce önerdiğimiz daha basit türü kullanarak, bileşenimizde bulduğumuz onChange yöntemini çeşitli şekillerde yazabiliriz:
const onChange1 = ( isChecked: boolean ): void => isChecked ? setOverlay( { color: '#000c', isEnabled: true } ) : setOverlay( { color: '#000c', isEnabled: false } ); const onChange2 = ( isChecked: boolean ): void => setOverlay( { color: '#000c', isEnabled: isChecked } ); const onChange3 = ( isChecked: boolean ): void => setOverlay( { ...overlay, isEnabled: isChecked } );
ama ne yazık ki hepsi doğru değil. Örneğin, üçüncü varyantta, overlay şu anda nasıl tanımlandığı hakkında hiçbir fikrimiz yok: onChange3 kullanarak kaplamayı etkinleştirirsek, overlay geçerli değerlerini ve dolayısıyla mevcut color kullanırdık. Ama şu anki rengi nedir? Değerinin doğru olduğundan emin miyiz? Kim başlattı? Ne zaman? Hangi değeri kullandılar? Aşağıdakilere sahipsek ne olur?
const overlay = { isEnabled: false, color: '', };
Ancak, önerdiğimiz ilk (birlik) tür, ayarı etkinleştirdiğimizde bizi açıkça bir color belirlemeye zorlar ve onChange3 çalışmaz. Bunun yerine, color için açıkça bir değer belirleyen bir fonksiyon yazmalıyız:
const onChange = ( isChecked: boolean ): void => isChecked ? setOverlay( { color: '#000c', isEnabled: true } ) : setOverlay( { isEnabled: false } );
Oldukça havalı, ha? Bunun basit bir örnek olduğunu biliyorum, ancak bu yaklaşımı her yerde takip ederek kaç tane hatayı önleyebileceğimizi hayal edebiliyor musunuz? Ve, hey, bunun için söz vermeyin – kullanıcılarımızın sonuçtan ne kadar mutlu olduklarına bakın:
Nelio Popup'ları (v1.0.6) kullanmaktan gerçekten zevk alıyorum. Geliştiriciler bu blok eklentisini tasarlarken iyi bir iş çıkardılar. Eklentide görebildiğim kullanıcı arayüzü ayrıntılarına gösterilen ilgiyi seviyorum. Daha fazla geliştiricinin, bir şeyi yapan, iyi yapan ve doğal olarak WordPress Blokları ekosistemine uyan Nelio Popup'lar gibi tek kullanımlık blok eklentileri oluşturmasını diliyorum.
WordPress.org'da TheFrameGuy
Çözüm
İyi bir veri modeli, kodumuzda temsil etmeye çalıştığımız gerçekliği güvenilir bir şekilde yakalayan modeldir. Yani, tüm gerçekliği temsil edecek kadar anlamlı bir modele ihtiyacımız var, ama artık değil. İmkansız durumları imkansız hale getirerek, derleyiciyi en iyi arkadaşımız yaparız, çünkü bu aptalca hatalar yapmamamızı sağlar.
Umarım bugünkü gönderiyi beğenmişsinizdir ve faydalı bir şeyler öğrenmişsinizdir. Paylaşmak istediğiniz herhangi bir içgörü veya şüpheniz varsa, aşağıdaki yorum bölümünde bana bildirin, yardımcı olmaktan memnuniyet duyarız.
Unsplash'ta Martin Wyall tarafından Öne Çıkan Resim.
ev borcu WordPress sitesi