API-uri React utile pentru construirea de componente flexibile cu TypeScript

Publicat: 2022-03-10
Rezumat rapid ↬ React with JSX este un instrument fantastic pentru realizarea de componente ușor de utilizat. Componentele Typescript fac ca dezvoltatorii să vă integreze componentele în aplicațiile lor și să vă exploreze API-urile într-o plăcere absolută. Aflați despre trei API-uri React mai puțin cunoscute care vă pot duce componentele la următorul nivel și vă pot ajuta să construiți componente React și mai bune în acest articol.

Ați folosit vreodată React.createElement direct? Ce zici de React.cloneElement ? React este mai mult decât transformarea JSX-ului în HTML. Mult mai mult și pentru a vă ajuta să vă îmbunătățiți cunoștințele despre API-uri mai puțin cunoscute (dar foarte utile) cu care este livrat biblioteca React. Vom trece peste câteva dintre ele și câteva dintre cazurile lor de utilizare care pot îmbunătăți drastic integrarea și utilitatea componentelor dvs.

În acest articol, vom trece peste câteva API-uri React utile care nu sunt la fel de cunoscute, dar extrem de utile pentru dezvoltatorii web. Cititorii ar trebui să aibă experiență cu sintaxa React și JSX, cunoștințele Typescript sunt utile, dar nu necesare. Cititorii vor pleca cu tot ce trebuie să știe pentru a îmbunătăți considerabil componentele React atunci când le folosesc în aplicațiile React.

React.cloneElement

Este posibil ca majoritatea dezvoltatorilor să nu fi auzit niciodată de cloneElement sau să-l fi folosit vreodată. A fost introdus relativ recent pentru a înlocui funcția cloneWithProps , acum depreciată. cloneElement un element, de asemenea, vă permite să îmbinați elemente de recuzită noi cu elementul existent, modificându-le sau suprascriindu-le după cum credeți de cuviință. Acest lucru deschide opțiuni extrem de puternice pentru construirea de API-uri de clasă mondială pentru componente funcționale. Aruncă o privire la semnătură.

 function cloneElement( element, props?, ...children)

Iată versiunea condensată Typescript:

 function cloneElement( element: ReactElement, props?: HTMLAttributes, ...children: ReactNode[]): ReactElement

Puteți să luați un element, să îl modificați, chiar să îi anulați copiii și apoi să îl returnați ca element nou. Aruncă o privire la următorul exemplu. Să presupunem că vrem să creăm o componentă TabBar a link-urilor. Ar putea arăta cam așa.

 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 este o listă de link-uri, dar avem nevoie de o modalitate de a defini două date, titlul link-ului și adresa URL. Deci, vom dori o structură de date transmisă cu aceste informații. Deci, dezvoltatorul nostru ar face componenta noastră așa.

 function App() { return ( <Tabbar links={[ {title: 'First', url: '/first'}, {title: 'Second', url: '/second'}] } /> ) }

Acest lucru este grozav, dar ce se întâmplă dacă utilizatorul dorește să redea elemente de button în loc a elemente? Ei bine, am putea adăuga o altă proprietate care îi spune componentului ce tip de element să redeze.

Dar puteți vedea cum acest lucru va deveni rapid greoi, ar trebui să acceptăm din ce în ce mai multe proprietăți pentru a gestiona diverse cazuri de utilizare și cazuri de margine pentru o flexibilitate maximă.

Iată o modalitate mai bună, folosind React.cloneElement .

Vom începe prin a ne schimba interfața pentru a face referire la tipul ReactNode . Acesta este un tip generic care cuprinde orice poate reda React, de obicei JSX Elements, dar poate fi și șiruri de caractere și chiar null . Acest lucru este util pentru a desemna că doriți să acceptați componentele React sau JSX ca argumente inline.

 export interface ITabbarProps { links: ReactNode[] }

Acum îi cerem utilizatorului să ne dea câteva elemente React și le vom reda așa cum dorim.

 function Tabbar(props: ITabbarProps) { return ( <> {props.links.map((e, i) => e // simply return the element itself )} </> ) }

Acest lucru este perfect valabil și ar reda elementele noastre. Dar uităm câteva lucruri. Pentru unul, key ! Dorim să adăugăm chei pentru ca React să ne poată reda listele în mod eficient. De asemenea, dorim să ne modificăm elementele pentru a face transformările necesare, astfel încât să se încadreze în stilul nostru, cum ar fi className și așa mai departe.

Le putem face cu React.cloneElement și o altă funcție React.isValidElement pentru verificarea conformității argumentului cu ceea ce ne așteptăm!

Mai multe după săritură! Continuați să citiți mai jos ↓

React.isValidElement

Această funcție returnează true dacă un element este un element React valid și React îl poate reda. Iată un exemplu de modificare a elementelor din exemplul anterior.

 function Tabbar(props: ITabbarProps) { return ( <> {props.links.map((e, i) => isValidElement(e) && cloneElement(e, {key: `${i}`, className: 'bold'}) )} </> ) }

Aici adăugăm un element cheie pentru fiecare element pe care îl transmitem și facem fiecare link îndrăzneț în același timp! Acum putem accepta elemente React arbitrare ca elemente de recuzită astfel:

 function App() { return ( <Tabbar links={[ <a href='/first'>First</a>, <button type='button'>Second</button> ]} /> ) }

Putem suprascrie oricare dintre elementele de recuzită setate pe un element și putem accepta cu ușurință diverse tipuri de elemente, făcând componenta noastră mult mai flexibilă și ușor de utilizat.

Avantajul aici este că dacă am dori să setăm un handler personalizat onClick pentru butonul nostru, am putea face acest lucru. Acceptarea elementelor React în sine ca argumente este o modalitate puternică de a oferi flexibilitate designului componentelor dumneavoastră.

useState Setter

Folosește Hooks! Cârligul useState este extrem de util și un API fantastic pentru crearea rapidă a stării componentelor dvs., astfel:

 const [myValue, setMyValue] = useState()

Datorită timpului de execuție JavaScript, poate avea unele sughițuri. Îți amintești de închideri?

În anumite situații, este posibil ca o variabilă să nu fie valoarea corectă din cauza contextului în care se află, cum ar fi în buclele for de obicei sau evenimentele asincrone. Acest lucru se datorează definiției lexicale. Când este creată o nouă funcție, domeniul lexical este păstrat. Deoarece nu există nicio funcție nouă, domeniul lexical al newVal nu este păstrat și astfel valoarea este dereferențiată de momentul în care este utilizată.

 setTimeout(() => { setMyValue(newVal) // this will not work }, 1000)

Ceea ce va trebui să faceți este să utilizați setter-ul ca funcție. Prin crearea unei noi funcții, referința la variabile este păstrată în domeniul lexical și currentVal este transmisă de React useState Hook însuși.

 setTimeout(() => { setMyValue((currentVal) => { return newVal }) }, 1000)

Acest lucru va asigura că valoarea dvs. este actualizată corect, deoarece funcția de setare este apelată în contextul corect. Ceea ce face React aici este să vă apeleze funcția în contextul corect pentru a avea loc o actualizare a stării React. Acest lucru poate fi folosit și în alte situații în care este util să acționați asupra valorii curente, React vă apelează funcția cu primul argument ca valoare curentă.

Notă : pentru citire suplimentară pe tema asincronizării și închiderilor, vă recomand să citiți „ useState Lazy Initialization And Function Updates” de Kent C. Dodds.

Funcții în linie JSX

Iată o demonstrație Codepen a unei funcții JSX inline:

Vezi stiloul [Hello World in React](https://codepen.io/smashingmag/pen/QWgQQKR) de Gaurav Khanna.

Vedeți Pen Hello World în React de Gaurav Khanna.

Nu este chiar un API React per spus.

JSX acceptă funcții inline și poate fi cu adevărat util pentru declararea unei logici simple cu variabile inline, atâta timp cât returnează un element JSX.

Iată un exemplu:

 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! </> ) }

Aici declarăm cod în interiorul JSX, putem rula cod arbitrar și tot ce trebuie să facem este să returnăm o funcție JSX pentru a fi redată.

O putem face condiționată sau pur și simplu facem ceva logică. Luați notă de parantezele din jurul funcției inline. De asemenea, în special aici, unde numim această funcție, am putea chiar să trecem un argument în această funcție din contextul înconjurător dacă dorim!

 })()}

Acest lucru poate fi util în situațiile în care doriți să acționați asupra unei structuri de date de colectare într-un mod mai complex decât permite un .map standard în interiorul unui element JSX.

 function App() { return ( <> {(() => { let str = '' for (let i = 0; i < 10; i++) { str += i } return (<p>{str}</p>) })()} </> ) }

Aici putem rula un cod pentru a parcurge un set de numere și apoi le putem afișa în linie. Dacă utilizați un generator de site static, cum ar fi Gatsby, acest pas ar fi și precalculat.

component extends type

Foarte utilă pentru crearea de componente prietenoase cu completarea automată, această caracteristică vă permite să creați componente care extind elementele HTMLElements existente sau alte componente. În mare parte utilă pentru tastarea corectă a unei interfețe de elemente în Typescript, dar aplicația reală este aceeași pentru JavaScript.

Iată un exemplu simplu, să presupunem că vrem să înlocuim una sau două proprietăți ale unui element de button , dar totuși le oferim dezvoltatorilor opțiunea de a adăuga alte proprietăți la butonul. Cum ar fi setarea type='button' sau type='submit' . Evident, nu vrem să recreăm întregul element buton, vrem doar să extindem proprietățile existente și poate să adăugăm încă o prop.

 import React, { ButtonHTMLAttributes } from 'react'

Mai întâi importăm React și clasa ButtonHTMLAttributes , un tip care cuprinde elementele de recuzită ale unui HTMLButtonElement . Puteți citi codul sursă pentru acest tip de interfață aici:

Și puteți vedea că echipa React a reimplementat toate API-urile web în TypeScript, astfel încât să poată fi verificate.

În continuare, declarăm interfața noastră astfel, adăugând proprietatea noastră status .

 interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> { status?: 'primary' | 'info' | 'danger' }

Și, în sfârșit, facem câteva lucruri. Folosim destructurarea ES6 pentru a scoate elementele de recuzită la care ne pasă ( status și children ) și pentru a declara orice alte proprietăți ca rest . Și în ieșirea noastră JSX, returnăm un element buton, cu structurare ES6 pentru a adăuga orice proprietăți suplimentare acestui element.

 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> ) }

Așa că acum un dezvoltator poate adăuga un type de prop sau orice alt element pe care un buton ar avea de obicei. Am oferit un suport suplimentar pe care l-am folosit în className pentru a seta stilul butonului.

Iată întregul exemplu:

 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> ) }

Acest lucru este o modalitate excelentă de a crea componente interne reutilizabile, care se conformează ghidurilor dvs. de stil, fără a reconstrui elemente HTML întregi! Pur și simplu puteți suprascrie elemente de recuzită întregi, cum ar fi setarea className pe baza stării sau permiteți transmiterea de nume de clasă suplimentare.

 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> ) }

Aici luăm prop className transmis elementului nostru Button și îl introducem înapoi, cu o verificare de siguranță în cazul în care prop nu este undefined .

Concluzie

React este o bibliotecă extrem de puternică și există un motiv bun pentru care a câștigat rapid popularitate. Vă oferă un set excelent de instrumente pentru a crea aplicații web performante și ușor de întreținut. Este extrem de flexibil și totuși foarte strict în același timp, ceea ce poate fi incredibil de util dacă știi cum să-l folosești. Acestea sunt doar câteva API-uri care sunt demne de remarcat și sunt în mare parte trecute cu vederea. Încearcă-le în următorul tău proiect!

Pentru a citi mai multe despre cele mai recente API-uri React, hooks, aș recomanda să citiți useHooks(). Cheatsheet Typescript are, de asemenea, câteva informații excelente pentru React și Typescript Hooks.