React:n hyvät ja huonot puolet
Miksi kannattaa käyttää React:tä? Mitä etuja JavaScript-kirjastolla on? Saat vastaukset selville sukeltamalla tähän artikkeliin ja tutustumalla React:n käytön todellisiin etuihin.
Opi yksinkertaistamaan ja parantamaan komponenttien näkyvyyttä React:ssä käyttämällä ehdollista renderöintiä ja suojauskomponentteja.
Tänään haluaisin keskustella siitä, miten hallita komponenttien näkyvyys React:ssä. Mutta ennen kuin aloitamme on pieni
vastuuvapauslauseke:
Esitelty ratkaisu ei ole kovin turvallinen, sillä se ei suojaa sovellusta "hakkereilta" (joita sitten onkin).
Muista, että sinun on suojattava päätepisteet ja käytettävä hyviä käytäntöjä sovellussuunnittelussa. Tämä ratkaisu vain
tekee työstäsi vain hieman helpompaa.
Yksi yleisimmistä toiminnoista on näyttää komponentti vain tietyille käyttäjille, joilla on tietyt oikeudet,
roolit tai oikeudet. Yleinen ratkaisu on lisätä joitakin jos
s:lle koodi, tarkista olosuhteet manuaalisesti ja näytä tai jätä elementit näyttämättä.
Katsotaanpa SimpleAppHeader
komponentti, joka sisältää vain vähän navigointielementtejä.
<!-- wp:paragraph -->
<p><code>typescript jsx<br>
export const SimpleAppHeader: FC = () => {<br>
return (<br>
<header><br>
<nav><br>
<ul><br>
{<br>
currentUser.role === "ADMIN" &&<br>
<li>VainAdmin-komponentti</li><br>
}<br>
{<br>
currentUser.role === "USER" &&<br>
<li>Ainoastaan käyttäjäkomponentti</li><br>
}<br>
{<br>
(currentUser.role === "ADMIN" || currentUser.role === "USER") &&<br>
<li>VainAdmin- ja User-komponentti</li><br>
}<br>
<li>Yleinen käyttöelementti</li><br>
</ul><br>
</nav><br>
</header><br>
)<br>
}<br>
</code></p>
<!-- /wp:paragraph -->
Näyttää "ihan hyvältä". Todennäköisesti tarkistukset voitaisiin kapseloida funktioihin monimutkaisuuden vähentämiseksi. currentUser
on jokin objekti
joka on rooli
ominaisuus, mutta se voi olla mikä tahansa, jota haluamme käyttää valvontamekanismimme kohteena.
Tässä koodissa on joitakin ongelmia. Jos projekti kasvaa käytämme luultavasti tätä rakennetta monissa paikoissa. Meidän on siis kopioitava,
jotenkin, olosuhteissa. Tätä koodia on vaikea ylläpitää ja muuttaa tulevaisuudessa. Varsinkin kun pääsysäännöt muuttuvat
aika esim. meidän on muutettava currentUser
joksikin muuksi. Sitä on hyvin vaikea testata. Sinun täytyy kirjoittaa monia testejä
vain varmistaaksesi, että tilasi on kunnossa.
On aika yksinkertaistaa tätä koodia hieman. Voisimme poimia joitakin metodeja ja tehdä koodista lyhyemmän ja vähemmän monimutkaisen:
const isAdmin = (role: string): boolean => role === "ADMIN";
const isUser = (role: string): boolean => role === "USER";
const isAdminOrUser = (role: string): boolean => isAdmin(rooli) || isUser(rooli);
export const SimplifiedAppHeader: FC = () => { {
return (
{
isAdmin(currentUser.role) &&
Vain admin-komponentti
}
{
isUser(currentUser.role) &&
Vain käyttäjä-komponentti
}
{
(isAdminOrUser(currentUser.role)) &&
Vain järjestelmänvalvojan ja käyttäjän komponentti
}
Yleinen käyttöelementti
)
}
```
Näyttää lupaavalta. Vähennämme melua od toista rivejä. Koodi on luettavampaa ja helpompi ylläpitää. Mutta katsokaa
toiminto isAdminOrUser
. Meillä on vain kaksi roolia. Jos otamme käyttöön kolmannen roolin, meidän on luotava toinen joukko toimintoja.
joka yhdistää rooleja. Aika toiseen versioon.
Otetaan käyttöön funktio hasRole
joka korvaa meidän isX
toiminnot.
const hasRole = (role: string, requiredRole: string[]): boolean => {
let found: string | undefined = requiredRole.find(s => role === s);
return found !== undefined;
}
export const FilteringAppHeader: FC = () => { {
return (
{
hasRole(currentUser.role, ["ADMIN"]) &&
Vain admin-komponentti
}
{
hasRole(currentUser.role, ["USER"]) &&
Vain käyttäjä-komponentti
}
{
hasRole(currentUser.role, ["ADMIN", "USER"]) &&
Vain admin- ja user-komponentti
}
Yleinen käyttöelementti
)
}
```
Nyt näyttää hyvältä. Meillä on edelleen ehtoja html-koodin osassa, mutta nyt voimme testata seuraavia asioita. hasRole
toiminto ja luottaa siihen, että se
käytetään oikeilla parametreilla. Roolien lisääminen tai muuttaminen on nyt helpompaa. Käytämme arraya, joka sisältää kaikki
roolit, joita tarvitsemme.
Tämä ratkaisu on kuitenkin sidottu currentUser
esine. Oletuksena on, että objekti on jotenkin globaali tai helposti saatavilla osoitteessa
missä tahansa sovelluksessa. Voimme siis yrittää kapseloida tämän. Voimme tietysti siirtää sen hasRole
toiminto:
const hasRole = (requiredRole: string[]): boolean => { {
let found: string | undefined = requiredRole.find(s => currentUser.role === s);
return found !== undefined;
}
Mutta se ei anna meille juuri mitään.
Vartija
KomponenttiAika luoda komponentti, joka kapseloi koko logiikan. Nimesin sen Vartija
ja haluan käyttää sitä näin.
export const GuardedAppHeader: FC = () => { {
return (
);
}
Komponentti tarvitsee kaksi ominaisuutta. Ensimmäinen lapset
vastuussa suojatusta sisällöstä. Toinen requiredRoles
että
käsitellä rooleja, jotka antavat meille pääsyn.
Tarvitsemme tämän rakenteen edustuksen. Se on hyvin yksinkertainen. Laajennamme tyypin React.PropsWithChildren (rekvisiitta lasten kanssa)
joka on lapset
omaisuus. Voit tietysti lisätä kyseisen ominaisuuden manuaalisesti tyyppiin ja jättää laajennuksen pois.
interface IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
}
Myös itse komponentti on yksinkertainen. Käytämme uudelleen hasRole
toiminto täällä.
export const Guard = (props: IGuardProps) => { {
const hasRole = (requiredRole: string[]): boolean => {
let found: string | undefined = requiredRole.find(s => currentUser.role === s);
return found !== undefined;
}
if (hasRole(props.requiredRoles)) {
return (
{props.children}
);
} else {
return ;
}
}
"`
Voisin sanoa, että lopeta tähän, mutta se olisi liian helppoa. Nyt meillä on komponentti, ja voimme käyttää sitä äärimmilleen 😀.
Ensimmäinen vaihe on tarkastusten ulkoistaminen. currentUser
on kovakoodattu arvo. Haluaisin kapseloida tämän arvon
johonkin palveluun, joka "osaa" tarkistaa roolit. Teknisesti se tarkoittaa, että siirretään hasRole
funktio toiseen
luokka.
Luon yksinkertaisen käyttöliittymän IGuardService
jolla on vain yksi ominaisuus - hasRole
.
export interface IGuardService {
checkRole: (roles: string[]) => boolean;
}
Yksinkertainen toteutus voisi näyttää tältä
class SimpleGuard implements IGuardService {
checkRole(roles: string[]): boolean {
let found: string | undefined = roles.find(e => e === currentUser.role);
return found !== undefined;
}
}
Käyttääksemme sitä meidän on muutettava IGuardProperties
ja käyttötapaus.
export interface IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
guardService: IGuardService;
}
// ...
const AppHeader: FC = () => { {
const guardService = new SimpleGuard();
return (
Vain ylläpitäjän komponentti
Vain käyttäjälle tarkoitettu komponentti
Ylläpitäjän ja käyttäjän komponentti
Yleinen käyttöelementti
);
}
```
Nyt komponentti näyttää seuraavalta:
export const Guard = (props: IGuardProps) => { {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} else {
return ;
}
}
Paljon parempi. GuardService
erottaa meidät logiikasta, joka tarkistaa roolit. Voimme muuttaa sitä ilman seurauksia meidän
komponentti. Yleinen käyttötapaus on käyttää mockia testeissä ja jotain "oikeaa" toteutusta tuotantokoodissa.
Seuraava parannus on mukautettujen Kielletty
elementti. Nykyinen ratkaisu tekee tyhjän elementin. Ensin meidän on
muutos IGuardProps
lisäämällä funktio, joka renderöidään kyseiselle elementille.
export interface IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
guardService: IGuardService;
forbidden?: () => React.ReactNode;
}
Tämä on valinnainen ominaisuus, jonka nimi päättyy kysymysmerkkiin. Se voi siis olla function tai undefined
. Meidän on
käsitellä sitä Vartija
komponentti.
export const Guard = (props: IGuardProps) => { {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} else if (props.forbidden === undefined) {
return ;
} else {
return (
{props.forbidden()}
);
}
}
// ...
export const AppHeader: FC = () => { {
const guardService = new SimpleGuard();
return (
Vain ylläpitäjän komponentti
Vain käyttäjälle tarkoitettu komponentti
Kielletty - vain moderaattori voi nähdä tämän.
}>
Moderaattori-komponentti
Ylläpitäjä- ja käyttäjäkomponentti
Yleinen käyttöelementti
);
}
```
Aika viimeiseen suureen muutokseen. Nykyisen version komponentti tukee vain rooleja merkkijono
. Meillä voisi olla useita erilaisia
kiinteistötyypit, jotka haluaisimme tarkistaa. Numerot tai mukautetut tyypit eivät ole mitään erityistä. Lisään geneeristen ominaisuuksien tuen.
Ensimmäinen vaihe on muutokset IGuardService
rajapinta. Toteutukset riippuvat testattavan arvon tyypistä. Se voi olla
olla mitä tahansa, joten käyttöliittymän pitäisi käsitellä sitä.
export interface IGuardService {
checkRole: (roles: ROLE[]) => boolean;
}
Nyt se ottaa array of ROOLI
geneerinen tyyppi. Yksinkertainen toteutuksemme muuttuu hieman.
class SimpleGuard implements IGuardService {
checkRole(roles: string[]): boolean {
let found: string | undefined = roles.find(e => e === currentUser.role);
return found !== undefined;
}
}
Meidän on lisättävä tyyppiparametri, mutta voisimme valmistella toteutuksen, joka tukee tyyppiä IRole
rajapinta.
rajapinta IRole {
name: string;
}
//...
class RoleGuardService implements IGuardService {
checkRole(roles: IRole[]): boolean {
let found: IRole | undefined = roles.find(e => e === userWithRole.role);
return found !== undefined;
}
}
```
Toinen vaihe on levittää tämä muutos IGuardProps
.
interface IGuardProps extends React.PropsWithChildren {
requiredRoles: ROLE[];
guardService: IGuardService;
forbidden?: () => React.ReactNode;
}
Ja vastaavasti Vartija
komponentti.
export const Guard = (props: IGuardProps) => { {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} else if (props.forbidden === undefined) {
return ;
} else {
return (
{props.forbidden()}
);
}
}
Ja käyttötapauksemme
export const AppHeader: FC = () => { {
const guardService = new SimpleGuard();
const roleService = new RoleGuardService();
return (
<header>
<nav>
<ul>
<Guard<IRole> requiredRoles={[{name: "ADMIN"}]} guardService={roleService}>
<li>Vain järjestelmänvalvojan komponentti</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>
<li>Vain käyttäjälle tarkoitettu komponentti</li>
</Guard>
<Guard<string> requiredRoles={["MODERATOR"]} guardService={guardService}
forbidden={() => <div>Kielletty - vain moderaattori voi nähdä tämän</div>}>
<li>Moderaattorikomponentti</li>
</Guard>
<Guard<string> requiredRoles={["USER", "ADMIN"]} guardService={guardService}>
<li>Ylläpitäjä- ja käyttäjäkomponentti</li>
</Guard>
<li>Yleinen käyttöelementti</li>
</ul>
</nav>
</header>
);
}
Tässä esimerkissä käytän molempia toteutuksia IGuardservice
vain havainnollistamistarkoituksessa. Todellisissa käyttötapauksissa
luultavasti käyttää vain yhtä.
The Vartija
voisi olla sisäkkäisiä. Muista vain, että pääsy ratkaistaan ulkoisimmasta instanssista käsin.
export const NestAppHeader: FC = () => { {
const guardService = new SimpleGuard();
return (
<header>
<nav>
<ul>
<Guard<string> requiredRoles={["USER", "ADMIN"]} guardService={guardService}>
<Guard<string> requiredRoles={["ADMIN"]} guardService={guardService}>
<li>Vain järjestelmänvalvojan komponentti</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>
<li>Vain käyttäjälle tarkoitettu komponentti</li>
</Guard>
<li>Ylläpitäjä- ja käyttäjäkomponentti</li>
<Guard<string> requiredRoles={["MODERATOR"]} guardService={guardService}
forbidden={() => <div>Kielletty - vain moderaattori voi nähdä tämän</div>}>
<li>Moderaattorikomponentti</li>
</Guard>
</Guard>
<li>Yleinen käyttöelementti</li>
</ul>
</nav>
</header>
);
}
Yllä olevassa esimerkissä Moderaattorikomponentti
ei voisi koskaan esiintyä, koska käyttäjä voi käsitellä vain yhtä roolia. Ensimmäinen Vartija
rajoitukset
roolit ADMIN
ja KÄYTTÄJÄ
, joten MODERAATTORI
ei koskaan läpäise ensimmäistä tarkastusta.
Voimme rakentaa erikoistuneita komponentteja, jotka piilottavat joitakin ominaisuuksia.
export const AdminGuard = (props: Omit) => { {
return >.
{props.children}
}
//...
export const SpecializedAppHeader: FC = () => { {
const guardService = new SimpleGuard();
return (
Vain ylläpitäjän komponentti
Vain käyttäjälle tarkoitettu komponentti
Forbidden - vain moderaattori voi nähdä tämän
}>
Moderaattori-komponentti
Ylläpitäjä- ja käyttäjäkomponentti
Yleinen käyttöelementti
);
}
```
Tässä tapauksessa AdminGuard
määrittele ennalta ADMIN
rooli. Seurauksena on, että meidän on nimenomaisesti määriteltävä tyyppi tyyppiä ROOLI
tyyppi
parametri.
Tässä artikkelissa näytän, miten luodaan ja käytetään Vartija
komponentti React:ssä. Aloitamme monimutkaisesta koodista, jota on vaikea
lukea ja ylläpitää. Kehitämme sen kehittäjäystävällisemmäksi ja otamme käyttöön mukautetun toiminnallisen komponentin. Seuraavaksi me
laajennetaan komponenttia lisäämällä lisätoimintoja, uudistetaan poimintapalvelua ja lisätään geneerisiä tyyppejä.
Lopuksi, meillä on komponentti, joka voidaan sisäkkäin on helppo testata ja ylläpitää.