Voor- en nadelen van React
Waarom is het de moeite waard om React te gebruiken? Welke voordelen heeft deze JavaScript bibliotheek? Om de antwoorden te weten te komen, duik in dit artikel en ontdek de echte voordelen van het gebruik van React.
Leer hoe je de zichtbaarheid van componenten kunt vereenvoudigen en verbeteren in React door gebruik te maken van conditionele rendering en bewakingscomponenten.
Vandaag wil ik bespreken hoe je zichtbaarheid van onderdelen in React. Maar voordat we beginnen is er een klein
disclaimer:
Gepresenteerde oplossing is niet erg veilig in de zin van bescherming van uw applicatie tegen "hackers" (wie er ook zijn).
Vergeet niet dat u uw eindpunten moet beschermen en goede werkwijzen moet gebruiken bij het ontwerpen van applicaties. Deze oplossing
maakt uw werk een beetje gemakkelijker.
Een van de meest voorkomende functionaliteiten is om een component alleen te tonen aan een aantal gebruikers die bepaalde rechten hebben,
rollen of privileges. Een gebruikelijke oplossing is om een aantal als
s naar codeControleer handmatig de voorwaarden en toon elementen of niet.
Laten we eens kijken naar EenvoudigeAppHeader
component die weinig navigatie-elementen bevat.
<!-- 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>Alleen beheerderscomponent</li><br>
}<br>
{<br>
currentUser.role === "USER" &&<br>
<li>Alleen gebruiker component</li><br>
}<br>
{<br>
(currentUser.role === "ADMIN" || currentUser.role == "USER") &&<br>
<li>Alleen admin en gebruiker component</li><br>
}<br>
<li>Algemeen gebruik element</li><br>
</ul><br>
</nav><br>
</header><br>
)<br>
}<br>
</code></p>
<!-- /wp:paragraph -->
Ziet er "niet slecht" uit. Waarschijnlijk zou je controles kunnen inkapselen in functies om de complexiteit te verminderen. huidige gebruiker
een object is
dat heeft rol
eigenschap, maar het kan van alles zijn dat we willen gebruiken als onderwerp van ons controlemechanisme.
Deze code heeft enkele problemen. Als project groeit gebruiken we die constructies waarschijnlijk op veel plaatsen. We moeten dus kopiëren,
op de een of andere manier, voorwaarden. Deze code is moeilijk te onderhouden en in de toekomst te veranderen. Vooral wanneer de toegangsregels veranderen tijdens
tijd bijv. we moeten veranderen huidige gebruiker
in iets anders. Het is erg moeilijk om te testen. Je moet veel tests schrijven
gewoon om te controleren of uw toestand in orde is.
Tijd om deze code een beetje te vereenvoudigen. We kunnen er enkele methoden uithalen en de code korter en minder complex maken:
const isAdmin = (role: string): boolean => role === "ADMIN";
const isUser = (rol: string): booleaans => rol === "USER";
const isAdminOrUser = (rol: string): boolean => isAdmin(rol) || isUser(rol);
export const SimplifiedAppHeader: FC = () => {
return (
{
isAdmin(currentUser.role) &&
Alleen admin component
}
{
isUser(currentUser.role) &&
Alleen gebruiker component
}
{
(isAdminOrUser(currentUser.role)) &&
Alleen admin en gebruiker component
}
Algemeen gebruik element
)
}
```
Ziet er veelbelovend uit. We verminderen ruis od herhalende regels. Code is leesbaarder en gemakkelijker te onderhouden. Maar kijk eens naar
functie isAdminOrUser
. We hebben maar twee rollen. Als we een derde rol introduceren, moeten we een andere set functies maken
die rollen combineert. Tijd voor een andere versie.
Laat de functie heeftRol
die onze isX
functies.
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"]) &&
Alleen beheerderscomponent
}
{
hasRole(currentUser.role, ["USER"]) &&
Alleen gebruiker component
}
{
hasRole(currentUser.role, ["ADMIN", "USER"]) &&
Alleen admin en gebruiker component
}
Algemeen gebruik element
)
}
```
Nu ziet het er goed uit. We hebben nog steeds voorwaarden in het html-gedeelte van de code, maar nu kunnen we testen heeftRol
functie en erop vertrouwen dat het
wordt gebruikt met de juiste set parameters. Rollen toevoegen of wijzigen is nu eenvoudiger. We gebruiken een array die alle
rollen die we nodig hebben.
Deze oplossing is echter gebonden aan huidige gebruiker
object. De aanname is dat het object op de een of andere manier globaal is of gemakkelijk toegankelijk in
elke plaats in de applicatie. Dus we kunnen proberen dit in te kapselen. Natuurlijk kunnen we het verplaatsen naar heeftRol
functie:
const hasRole = (requiredRole: string[]): boolean => {
let found: string | undefined = requiredRole.find(s => currentUser.role === s);
return found !== undefined;
}
Maar dat geeft ons bijna niets.
Bewaker
ComponentTijd om een component te maken dat de hele logica inkapselt. Ik heb het Bewaker
en dit is hoe ik het wil gebruiken.
export const GuardedAppHeader: FC = () => {
return (
);
}
Component heeft twee eigenschappen nodig. Eerste kinderen
verantwoordelijk voor inhoud die wordt bewaakt. Tweede requiredRoles
dat
array van rollen afhandelen die ons toegang geeft.
We hebben een representatie van deze struct nodig. Het is heel eenvoudig. We breiden type React.PropsMetKinderen
dat heeft kinderen
eigenschap. Natuurlijk kun je die eigenschap handmatig toevoegen aan je type en de extensie weglaten.
interface IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
}
Het onderdeel zelf is ook eenvoudig. We hergebruiken heeftRol
functie hier.
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 ;
}
}
"`
En ik zou kunnen zeggen: stop hier, maar dat zou te gemakkelijk zijn. Nu hebben we component, en we kunnen het tot het uiterste gebruiken 😀
De eerste stap is de externalisering van controles. huidige gebruiker
is een hardgecodeerde waarde. Ik wil deze waarde inkapselen
in een of andere service, die "weet" hoe hij rollen moet verifiëren. Technisch gezien betekent dit dat we heeftRol
functie naar een andere
klasse.
Ik creëer een eenvoudige interface IGuardService
die slechts één eigenschap heeft - heeftRol
.
exportinterface IGuardService {
checkRole: (rollen: string[]) => boolean;
}
Nu zou een eenvoudige implementatie er als volgt uit kunnen zien
Klasse SimpleGuard implementeert IGuardService {
checkRole(roles: string[]): boolean {
let found: string | undefined = roles.find(e => e === currentUser.role);
return found !.== undefined;
}
}
Om het te gebruiken moeten we IGuardProperties
en use case.
export interface IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
guardService: IGuardService;
}
// ...
const AppHeader: FC = () => {
const guardService = nieuwe SimpleGuard();
return (
Alleen admin component
Alleen gebruiker component
Beheerder- en gebruikerscomponent
Algemeen gebruikselement
);
}
```
Nu ziet component eruit als:
export const Guard = (props: IGuardProps) => {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} else {
return ;
}
}
Veel beter. GuardService
ons scheiden van de logica die rollen controleert. We kunnen het veranderen zonder gevolgen voor onze
component. Gebruikelijk is om mock te gebruiken in tests en een "echte" implementatie in productiecode.
De volgende verbetering is het verwerken van aangepaste Verboden
element. De huidige oplossing geeft een leeg element weer. Eerst moeten we
veranderen IGuardProps
functie toevoegen die dat element zal renderen.
export interface IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
guardService: IGuardService;
forbidden?: () => React.ReactNode;
}
Dit is een optionele eigenschap, de naam eindigt met een vraagteken. Dus het kan een functie of ongedefinieerd
. We moeten
behandelen in Bewaker
component.
export const Guard = (props: IGuardProps) => {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} anders als (props.forbidden === undefined) {
return ;
} anders {
return (
{props.forbidden()}
);
}
}
// ...
export const AppHeader: FC = () => {
const guardService = nieuwe SimpleGuard();
return (
Alleen beheercomponent
Alleen gebruiker component
Verboden - alleen moderator kan dit zien
}>
Moderator component
Admin- en gebruikerscomponent
Algemeen gebruikselement
);
}
```
Tijd voor de laatste grote verandering. De huidige versie ondersteunt alleen rollen als string
. We zouden meerdere verschillende
typen eigenschappen die we willen controleren. Getallen of aangepaste typen is niets bijzonders. Ik zal generieke ondersteuning toevoegen.
De eerste stap is veranderingen in IGuardService
interface. Implementaties zijn afhankelijk van het type geteste waarde. Het kan
alles zijn, dus interface moet het afhandelen.
exporteer interface IGuardService {
checkRole: (roles: ROLE[]) => boolean;
}
Nu neemt het array van ROL
generiek type. Onze eenvoudige implementatie zal iets veranderen.
klasse SimpleGuard implementeert IGuardService {
checkRole(roles: string[]): boolean {
let found: string | undefined = roles.find(e => e === currentUser.role);
return found !.== undefined;
}
}
We moeten een typeparameter toevoegen, maar we kunnen een implementatie voorbereiden die ondersteuning biedt voor IRool
interface.
interface IRole {
naam: string;
}
//...
Klasse RolGuardService implementeert IGuardService {
checkRole(rollen: IRole[]): boolean {
let found: IRole | undefined = roles.find(e => e === userWithRole.role);
return found !.== undefined;
}
}
```
De tweede stap is om deze wijziging door te voeren naar IGuardProps
.
interface IGuardProps extends React.PropsWithChildren {
requiredRoles: ROLE[];
guardService: IGuardService;
forbidden?: () => React.ReactNode;
}
En respectievelijk naar Bewaker
component.
export const Guard = (props: IGuardProps) => {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} anders als (props.forbidden === undefined) {
return ;
} anders {
return (
{props.forbidden()}
);
}
}
En onze use cases
export const AppHeader: FC = () => {
const guardService = nieuwe SimpleGuard();
const roleService = nieuwe RoleGuardService();
return (
<header>
<nav>
<ul>
<Guard<IRole> requiredRoles={[{naam: "ADMIN"}]} guardService={roleService}>
<li>Alleen beheercomponent</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>
<li>Alleen voor de gebruiker</li>
</Guard>
<Guard<string> requiredRoles={["MODERATOR"]} guardService={guardService}
forbidden={() => <div>Verboden - alleen moderator kan dit zien</div>}>
<li>Moderator-component</li>
</Guard>
<Guard<string> requiredRoles={["USER", "ADMIN"]} guardService={guardService}>
<li>Admin- en gebruikerscomponent</li>
</Guard>
<li>Algemeen gebruikselement</li>
</ul>
</nav>
</header>
);
}
In dit voorbeeld gebruik ik beide implementaties van IGuardservice
alleen ter illustratie. In echte gebruikssituaties kun je
waarschijnlijk maar één gebruiken.
De Bewaker
kan genest worden. Vergeet niet dat toegang wordt opgelost in volgorde van de meeste externe instanties.
export const NestAppHeader: FC = () => {
const guardService = nieuwe SimpleGuard();
return (
<header>
<nav>
<ul>
<Guard<string> requiredRoles={["USER", "ADMIN"]} guardService={guardService}>
<Guard<string> requiredRoles={["ADMIN"]} guardService={guardService}>
<li>Alleen beheercomponent</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>
<li>Alleen voor de gebruiker</li>
</Guard>
<li>Admin- en gebruikerscomponent</li>
<Guard<string> requiredRoles={["MODERATOR"]} guardService={guardService}
forbidden={() => <div>Verboden - alleen moderator kan dit zien</div>}>
<li>Moderator-component</li>
</Guard>
</Guard>
<li>Algemeen gebruikselement</li>
</ul>
</nav>
</header>
);
}
In bovenstaand voorbeeld Moderator-component
kan nooit verschijnen, omdat een gebruiker maar één rol aankan. Eerste Bewaker
beperkingen
rollen naar ADMIN
en GEBRUIKER
dus MODERATOR
zal nooit door de eerste controle komen.
We kunnen gespecialiseerde componenten bouwen die sommige eigenschappen verbergen.
export const AdminGuard = (props: Omit) => {
return
{props.children}
}
//...
export const SpecializedAppHeader: FC = () => {
const guardService = nieuwe SimpleGuard();
return (
Alleen beheercomponent
Alleen gebruiker component
Verboden - alleen moderator kan dit zien
}>
Moderator component
Admin en gebruiker component
Algemeen gebruikselement
);
}
```
In dit geval AdminGuard
vooraf definiëren ADMIN
rol. Als gevolg hiervan moeten we het type van ROL
type
parameter.
In dit artikel laat ik je zien hoe je het volgende kunt maken en gebruiken Bewaker
component in React. We gaan uit van complexe code die moeilijk te
lezen en onderhouden. We evolueren het naar een ontwikkelaarvriendelijkere staat en introduceren aangepaste functionele componenten. Vervolgens
component uitbreiden met extra functionaliteiten, service extraheren en generieke types toevoegen.
Ten slotte hebben we een component dat genest kan worden en gemakkelijk te testen en te onderhouden is.