TypeScript ile Esnek Bileşenler Oluşturmak İçin Kullanışlı React API'leri
Yayınlanan: 2022-03-10 Hiç doğrudan React.createElement
kullandınız mı? Peki ya React.cloneElement
? React, JSX'inizi HTML'ye dönüştürmekten daha fazlasıdır. Çok daha fazlası ve React kitaplığının birlikte geldiği daha az bilinen (ancak çok kullanışlı) API'ler hakkındaki bilginizi artırmanıza yardımcı olmak için. Bileşenlerinizin entegrasyonunu ve kullanışlılığını büyük ölçüde artırabilecek birkaç tanesini ve kullanım durumlarından bazılarını gözden geçireceğiz.
Bu makalede, yaygın olarak bilinmeyen ancak web geliştiricileri için son derece yararlı olan birkaç kullanışlı React API'sini gözden geçireceğiz. Okuyucular React ve JSX sözdizimi konusunda deneyimli olmalıdır, TypeScript bilgisi faydalıdır ancak gerekli değildir. Okuyucular, React bileşenlerini React uygulamalarında kullanırken büyük ölçüde geliştirmek için bilmeleri gereken her şeyi öğrenecekler.
React.cloneElement
Çoğu geliştirici, cloneElement
hiç duymamış veya hiç kullanmamış olabilir. Artık kullanımdan kaldırılan cloneWithProps
işlevini değiştirmek için nispeten yakın zamanda tanıtıldı. cloneElement
bir öğeyi klonlar, ayrıca yeni sahne öğelerini mevcut öğeyle birleştirmenize, bunları değiştirmenize veya uygun gördüğünüz şekilde geçersiz kılmanıza olanak tanır. Bu, işlevsel bileşenler için birinci sınıf API'ler oluşturmak için son derece güçlü seçenekler sunar. İmzaya bir göz atın.
function cloneElement( element, props?, ...children)
İşte yoğun TypeScript sürümü:
function cloneElement( element: ReactElement, props?: HTMLAttributes, ...children: ReactNode[]): ReactElement
Bir öğeyi alabilir, değiştirebilir, hatta alt öğelerini geçersiz kılabilir ve ardından onu yeni bir öğe olarak geri döndürebilirsiniz. Aşağıdaki örneğe bir göz atın. Diyelim ki bağlantıların bir TabBar bileşeni oluşturmak istiyoruz. Bunun gibi bir şey görünebilir.
export interface ITabbarProps { links: {title: string, url: string}[] } export default function Tabbar(props: ITabbarProps) { return ( <> {props.links.map((e, i) => <a key={i} href={e.url}>{e.title}</a> )} </> ) }
TabBar bir bağlantı listesidir, ancak iki parça veriyi, bağlantının başlığını ve URL'yi tanımlamanın bir yoluna ihtiyacımız var. Bu nedenle, bu bilgilerle birlikte bir veri yapısının iletilmesini isteyeceğiz. Böylece geliştiricimiz bileşenimizi böyle yapacaktı.
function App() { return ( <Tabbar links={[ {title: 'First', url: '/first'}, {title: 'Second', url: '/second'}] } /> ) }
Bu harika, ancak kullanıcı a
öğe yerine button
öğelerini oluşturmak isterse ne olur? Bileşene ne tür bir öğe oluşturacağını söyleyen başka bir özellik ekleyebiliriz.
Ancak bunun nasıl hızla hantallaşacağını görebilirsiniz, maksimum esneklik için çeşitli kullanım durumlarını ve uç durumları ele almak için giderek daha fazla özelliği desteklememiz gerekecek.
İşte React.cloneElement
kullanarak daha iyi bir yol.
ReactNode
türüne referans verecek şekilde değiştirerek başlayacağız. Bu, React'in oluşturabileceği her şeyi, tipik olarak JSX Elements'i kapsayan genel bir türdür, ancak aynı zamanda dizeler ve hatta null
olabilir. Bu, React bileşenlerini veya JSX'i satır içi argümanlar olarak kabul etmek istediğinizi belirtmek için kullanışlıdır.
export interface ITabbarProps { links: ReactNode[] }
Şimdi kullanıcıdan bize bazı React Elements vermesini istiyoruz ve onları istediğimiz gibi oluşturacağız.
function Tabbar(props: ITabbarProps) { return ( <> {props.links.map((e, i) => e // simply return the element itself )} </> ) }
Bu tamamen geçerlidir ve öğelerimizi oluşturur. Ama birkaç şeyi unutuyoruz. Birincisi, key
! React'in listelerimizi verimli bir şekilde oluşturabilmesi için anahtar eklemek istiyoruz. Ayrıca, className
vb. gibi stilimize uymaları için gerekli dönüşümleri yapmak için öğelerimizi değiştirmek istiyoruz.
Bunları React.cloneElement
ile yapabiliriz ve argümanın beklediğimize uygun olup olmadığını kontrol etmek için başka bir React.isValidElement
işlevi!
React.isValidElement
Bu işlev, bir öğe geçerli bir React Öğesiyse ve React onu oluşturabiliyorsa true
değerini döndürür. İşte önceki örnekteki öğelerin değiştirilmesine bir örnek.
function Tabbar(props: ITabbarProps) { return ( <> {props.links.map((e, i) => isValidElement(e) && cloneElement(e, {key: `${i}`, className: 'bold'}) )} </> ) }
Burada, aktardığımız her öğeye bir anahtar destek ekliyoruz ve her bağlantıyı aynı anda kalın yapıyoruz! Artık isteğe bağlı React Elements'i şu şekilde sahne malzemesi olarak kabul edebiliriz:
function App() { return ( <Tabbar links={[ <a href='/first'>First</a>, <button type='button'>Second</button> ]} /> ) }
Bir öğe üzerinde ayarlanan herhangi bir sahne öğesini geçersiz kılabiliriz ve bileşenimizi çok daha esnek ve kullanımı kolay hale getiren çeşitli öğe türlerini kolayca kabul edebiliriz.
“
Buradaki avantaj, düğmemize özel bir onClick
işleyicisi ayarlamak isteseydik, bunu yapabilirdik. React öğelerinin kendilerini argüman olarak kabul etmek, bileşen tasarımınıza esneklik kazandırmanın güçlü bir yoludur.
useState
Ayarlayıcı İşlevi
Kanca kullanın! useState
kancası, bileşenlerinize hızlı bir şekilde durumu oluşturmak için son derece kullanışlıdır ve harika bir API'dir:
const [myValue, setMyValue] = useState()
JavaScript çalışma zamanı nedeniyle, bazı aksaklıklar olabilir. Kapanışları hatırlıyor musun?
Belirli durumlarda, bir değişken, yaygın olarak kullanılan for döngülerinde veya eşzamansız olaylarda olduğu gibi, içinde bulunduğu bağlam nedeniyle doğru değer olmayabilir. Bunun nedeni sözcüksel kapsam belirlemedir. Yeni bir işlev oluşturulduğunda sözcüksel kapsam korunur. Yeni bir işlev olmadığı için, newVal
sözcüksel kapsamı korunmaz ve bu nedenle, kullanıldığı zamana göre değer aslında başvurudan çıkarılır.
setTimeout(() => { setMyValue(newVal) // this will not work }, 1000)
Yapmanız gereken, ayarlayıcıyı bir işlev olarak kullanmaktır. Yeni bir fonksiyon yaratıldığında, değişken referansı sözlüksel kapsamda korunur ve currentVal, React useState Hook'un kendisi tarafından iletilir.
setTimeout(() => { setMyValue((currentVal) => { return newVal }) }, 1000)
Bu, ayarlayıcı işlevi doğru bağlamda çağrıldığı için değerinizin doğru şekilde güncellenmesini sağlayacaktır. React'in burada yaptığı, bir React durum güncellemesinin gerçekleşmesi için işlevinizi doğru bağlamda çağırmaktır. Bu, geçerli değere göre hareket etmenin yararlı olduğu diğer durumlarda da kullanılabilir, React, işlevinizi geçerli değer olarak ilk argümanla çağırır.
Not : Async ve closure'lar hakkında ek bilgi için Kent C. Dodds'un “ useState
Lazy Initialization And Function Updates” kitabını okumanızı tavsiye ederim.
JSX Satır İçi İşlevler
İşte bir JSX satır içi işlevinin bir Codepen demosu:
Tam olarak bir React API'si değil.
JSX satır içi işlevleri destekler ve bir JSX Öğesi döndürdüğü sürece satır içi değişkenlerle basit mantığı bildirmek için gerçekten yararlı olabilir.
“
İşte bir örnek:
function App() { return ( <> {(() => { const darkMode = isDarkMode() if (darkMode) { return ( <div className='dark-mode'></div> ) } else { return ( <div className='light-mode'></div> ) // we can declare JSX anywhere! } })()} // don't forget to call the function! </> ) }
Burada JSX'in içinde kod bildiriyoruz, rastgele kod çalıştırabiliriz ve tek yapmamız gereken, oluşturulacak bir JSX işlevi döndürmek.
Bunu koşullu yapabiliriz veya sadece biraz mantık yürütebiliriz. Satır içi işlevi çevreleyen parantezlere dikkat edin. Ayrıca özellikle burada bu işlevi çağırdığımız yerde, istersek çevreleyen bağlamdan bu işleve bir argüman bile iletebiliriz!
})()}
Bu, bir koleksiyon veri yapısı üzerinde standart bir .map
bir JSX öğesinin içinde izin verdiğinden daha karmaşık bir şekilde hareket etmek istediğiniz durumlarda faydalı olabilir.
function App() { return ( <> {(() => { let str = '' for (let i = 0; i < 10; i++) { str += i } return (<p>{str}</p>) })()} </> ) }
Burada, bir dizi sayı arasında dolaşmak için bazı kodlar çalıştırabilir ve ardından bunları satır içinde görüntüleyebiliriz. Gatsby gibi statik bir site oluşturucu kullanıyorsanız, bu adım da önceden hesaplanır.
component extends type
Otomatik tamamlama dostu bileşenler oluşturmak için son derece yararlı olan bu özellik, mevcut HTMLElements
veya diğer bileşenleri genişleten bileşenler oluşturmanıza olanak tanır. TypeScript'te bir öğe arayüzünü doğru yazmak için çoğunlukla kullanışlıdır, ancak asıl uygulama JavaScript için aynıdır.
İşte basit bir örnek, diyelim ki bir button
öğesinin bir veya iki özelliğini geçersiz kılmak istiyoruz, ancak yine de geliştiricilere düğmeye başka özellikler ekleme seçeneği sunuyoruz. type='button'
veya type='submit'
ayarı gibi. Açıkçası tüm düğme öğesini yeniden oluşturmak istemiyoruz, sadece mevcut özelliklerini genişletmek ve belki bir tane daha prop eklemek istiyoruz.
import React, { ButtonHTMLAttributes } from 'react'
İlk önce, HTMLButtonElement öğesinin özelliklerini kapsayan bir tür olan React ve ButtonHTMLAttributes
sınıfını içe HTMLButtonElement
. Bu tür bir arayüz için kaynak kodunu buradan okuyabilirsiniz:
Ayrıca, React ekibinin web'in tüm API'lerini TypeScript'te yeniden uyguladığını görebilirsiniz, böylece tip kontrolü yapılabilir.
Ardından, status
özelliğimizi ekleyerek arayüzümüzün böyle olduğunu bildiririz.
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { status?: 'primary' | 'info' | 'danger' }
Ve son olarak, birkaç şey yapıyoruz. Önem verdiğimiz donanımları ( status
ve children
) çıkarmak ve diğer özellikleri rest
olarak bildirmek için ES6 yıkımını kullanırız. Ve JSX çıktımızda, bu öğeye herhangi bir ek özellik eklemek için ES6 yapılandırmasına sahip bir düğme öğesi döndürüyoruz.
function Button(props: ButtonProps) { const { status, children, ...rest } = props // rest has any other props return ( <button className={`${status}`} {...rest} // we pass the rest of the props back into the element > {children} </button> ) }
Artık bir geliştirici, bir type
prop veya bir düğmenin tipik olarak sahip olacağı başka bir prop ekleyebilir. Düğmenin stilini ayarlamak için className
kullandığımız ek bir destek verdik.
İşte örneğin tamamı:
import React, { ButtonHTMLAttributes } from 'react' export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { status?: 'primary' | 'info' | 'danger' } export default function Button(props: ButtonProps) { const { status, children, ...rest } = props return ( <button className={`${status}`} {...rest} > {children} </button> ) }
Bu, tüm HTML öğelerini yeniden oluşturmadan stil yönergelerinize uyan yeniden kullanılabilir dahili bileşenler oluşturmanın harika bir yolunu sunar! Duruma göre className
ayarlamak veya ek sınıf adlarının da iletilmesine izin vermek gibi tüm aksesuarları geçersiz kılabilirsiniz.
import React, { ButtonHTMLAttributes } from 'react' export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { status?: 'primary' | 'info' | 'danger' } export default function Button(props: ButtonProps) { const { status, children, className, ...rest } = props return ( <button className={`${status || ''} ${className || ''}`} {...rest} > {children} </button> ) }
Burada, Button öğemize iletilen prop className
alıyoruz ve prop'un undefined
olması durumunda bir güvenlik kontrolü ile onu tekrar ekliyoruz.
Çözüm
React son derece güçlü bir kütüphanedir ve hızla popülerlik kazanmasının iyi bir nedeni vardır. Performanslı ve bakımı kolay web uygulamaları oluşturmak için size harika bir araç seti sunar. Son derece esnek ve aynı zamanda çok katıdır; bu, nasıl kullanılacağını biliyorsanız inanılmaz derecede faydalı olabilir. Bunlar, dikkate değer ve büyük ölçüde gözden kaçan sadece birkaç API'dir. Bir sonraki projenizde onlara bir şans verin!
En son React API'leri, kancalar hakkında daha fazla bilgi için useHooks() okumanızı tavsiye ederim. Typescript Cheatsheet'te ayrıca React ve Typescript Hooks için harika bilgiler var.