Pro e contro dell'React
Perché vale la pena utilizzare la libreria React? Quali vantaggi offre questa libreria JavaScript? Per trovare le risposte, immergetevi in questo articolo e scoprite i reali vantaggi dell'uso dell'React.
Scoprite come semplificare e migliorare la visibilità dei componenti in React utilizzando il rendering condizionale e i componenti di protezione.
Oggi vorrei parlare di come controllare visibilità dei componenti in React. Ma prima di iniziare c'è una piccola
esclusione di responsabilità:
La soluzione presentata non è molto sicura nel senso di proteggere l'applicazione dagli "hacker" (chiunque essi siano).
Ricordate che è necessario proteggere gli endpoint e utilizzare buone pratiche nella progettazione delle applicazioni. Questa soluzione
rende il vostro lavoro un po' più facile.
Una delle funzionalità più comuni è quella di mostrare il componente solo per un gruppo di utenti che hanno alcuni diritti specifici,
ruoli o privilegi. La soluzione più comune è quella di aggiungere alcuni se
s a codice, controllare manualmente le condizioni e mostrare o meno gli elementi.
Diamo un'occhiata a Intestazione dell'applicazione semplice
che contiene pochi elementi di navigazione.
<!-- wp:paragraph -->
<p><code>dattiloscritto jsx<br>
export const SimpleAppHeader: FC = () => {<br>
return (<br>
<header><br>
<nav><br>
<ul><br>
{<br>
currentUser.role === "ADMIN" &&<br>
<li>Componente solo ADMIN</li><br>
}<br>
{<br>
currentUser.role === "USER" &&<br>
<li>Componente solo utente</li><br>
}<br>
{<br>
(currentUser.role === "ADMIN" || currentUser.role === "USER") &&<br>
<li>Componente solo amministratore e utente</li><br>
}<br>
<li>Elemento di uso generale</li><br>
</ul><br>
</nav><br>
</header><br>
)<br>
}<br>
</code></p>
<!-- /wp:paragraph -->
Sembra "non male". Probabilmente si potrebbero incapsulare i controlli in funzioni per ridurre la complessità. utente corrente
è un oggetto
che ha ruolo
ma può essere qualsiasi cosa che si voglia utilizzare come oggetto del nostro meccanismo di controllo.
Questo codice presenta alcuni problemi. Se progetto cresce, probabilmente usiamo queste costruzioni in molti luoghi. Quindi dobbiamo copiare,
in qualche modo, le condizioni. Questo codice è difficile da mantenere e da modificare in futuro. Soprattutto quando le regole di accesso cambiano durante
tempo, ad esempio, dobbiamo cambiare utente corrente
in qualcos'altro. È molto difficile da testare. È necessario scrivere molti test
solo per verificare se le condizioni sono corrette.
È ora di semplificare un po' questo codice. Possiamo estrarre alcuni metodi e rendere il codice più breve e meno complesso:
const isAdmin = (role: string): booleano => role === "ADMIN";
const isUser = (role: string): booleano => role === "USER";
const isAdminOrUser = (role: string): booleano => isAdmin(role) || isUser(role);
export const SimplifiedAppHeader: FC = () => {
return (
{
isAdmin(currentUser.role) &&
Componente solo amministratore
}
{
isUser(currentUser.role) &&
Componente solo utente
}
{
(isAdminOrUser(currentUser.role)) &&
Componente solo Admin e Utente
}
Elemento di uso generale
)
}
```
Sembra promettente. Riduciamo il rumore e le linee ripetute. Il codice è più leggibile e più facile da mantenere. Ma date un'occhiata a
funzione isAdminOrUser
. Abbiamo solo due ruoli. Se introduciamo un terzo ruolo, dobbiamo creare un altro insieme di funzioni
che combina i ruoli. È tempo di un'altra versione.
Introduciamo la funzione haRuolo
che sostituirà il nostro èX
funzioni.
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"]) &&
Componente solo amministratore
}
{
hasRole(currentUser.role, ["USER"]) &&
Componente solo utente
}
{
hasRole(currentUser.role, ["ADMIN", "USER"]) &&
Componente solo per amministratori e utenti
}
Elemento di uso generale
)
}
```
Ora sembra buono. Abbiamo ancora delle condizioni nella parte html del codice, ma ora possiamo testare haRuolo
funzione e la fiducia che essa
verrà utilizzato con l'insieme corretto di parametri. L'aggiunta o la modifica dei ruoli è ora più semplice. Utilizziamo un array che contiene tutti i ruoli
ruoli di cui abbiamo bisogno.
Tuttavia, questa soluzione è vincolata a utente corrente
oggetto. Il presupposto è che l'oggetto sia in qualche modo globale o facilmente accessibile in
in qualsiasi punto dell'applicazione. Quindi possiamo provare a incapsulare questo aspetto. Naturalmente, possiamo spostarlo in haRuolo
funzione:
const hasRole = (requiredRole: string[]): boolean => {
let found: string | undefined = requiredRole.find(s => currentUser.role === s);
return found !== undefined;
}
Ma questo non ci dà quasi nulla.
Guardia
ComponenteÈ ora di creare un componente che incapsuli l'intera logica. L'ho chiamato Guardia
e questo è il modo in cui voglio usarlo.
export const GuardedAppHeader: FC = () => {
return (
);
}
Il componente ha bisogno di due proprietà. La prima bambini
responsabile dei contenuti protetti. Secondo ruoli richiesti
che
gestire una serie di ruoli che ci danno accesso.
Abbiamo bisogno di una rappresentazione di questa struttura. È molto semplice. Estendiamo il tipo React.PropsConBambini
che ha bambini
proprietà. Naturalmente, è possibile aggiungere manualmente questa proprietà al tipo e omettere l'estensione.
interfaccia IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
}
Anche il componente in sé è semplice. Riutilizzeremo haRuolo
funzione qui.
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 {
restituisce ;
}
}
"`
Potrei dire di fermarsi qui, ma sarebbe troppo facile. Ora abbiamo un componente, e possiamo usarlo fino all'estremo 😀
Il primo passo sarà l'esternalizzazione dei controlli. utente corrente
è un valore codificato. Vorrei incapsulare questo valore
in qualche servizio che "saprà" come verificare i ruoli. Tecnicamente questo significa che spostiamo haRuolo
ad un'altra funzione
classe.
Creo un'interfaccia semplice IGuardService
che ha solo una proprietà - haRuolo
.
Esportare l'interfaccia IGuardService {
checkRole: (roles: string[]) => boolean;
}
Una semplice implementazione potrebbe essere la seguente
classe SimpleGuard implementa IGuardService {
checkRole(roles: string[]): boolean {
let found: string | undefined = roles.find(e => e === currentUser.role);
return found !== undefined;
}
}
Per utilizzarlo è necessario modificare IGuardProperties
e caso d'uso.
Esportare l'interfaccia IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
guardService: IGuardService;
}
// ...
const AppHeader: FC = () => {
const guardService = new SimpleGuard();
return (
Componente solo per l'amministratore
Componente per soli utenti
Componente amministratore e utente
Elemento di utilizzo generale
);
}
```
Ora il componente si presenta come:
export const Guard = (props: IGuardProps) => {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} else {
restituisce ;
}
}
Molto meglio. Servizio di guardia
ci separa dalla logica che controlla i ruoli. Possiamo cambiarla senza conseguenze per il nostro
componente. Il caso d'uso comune è quello di utilizzare i mock nei test e alcune implementazioni "reali" nel codice di produzione.
Il prossimo miglioramento riguarderà la gestione delle informazioni personalizzate Vietato
elemento. La soluzione attuale rende l'elemento vuoto. Per prima cosa è necessario
cambiamento Oggetti IGuard
aggiungendo la funzione che verrà resa per quell'elemento.
export interface IGuardProps extends React.PropsWithChildren {
requiredRoles: string[];
guardService: IGuardService;
forbidden?: () => React.ReactNode;
}
Si tratta di una proprietà opzionale, il cui nome termina con il carattere punto interrogativo. Quindi potrebbe essere una funzione o indefinito
. Abbiamo bisogno di
gestire il tutto in Guardia
componente.
export const Guard = (props: IGuardProps) => {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} else if (props.forbidden === undefined) {
restituisce ;
} else {
return (
{props.forbidden()}
);
}
}
// ...
export const AppHeader: FC = () => {
const guardService = new SimpleGuard();
return (
Componente solo per l'amministratore
Componente per soli utenti
Vietato - solo il moderatore può vedere questo componente
}>
Componente moderatore
Componente amministratore e utente
Elemento di utilizzo generale
);
}
```
È il momento dell'ultimo grande cambiamento. La versione attuale del componente supporta solo i ruoli come stringa
. Potremmo avere diverse
tipi di proprietà che si desidera controllare. Numeri o tipi personalizzati non sono niente di speciale. Aggiungerò il supporto dei generici.
Il primo passo è la modifica della IGuardService
interfaccia. Le implementazioni dipendono dal tipo di valore testato. Potrebbe
essere qualsiasi cosa, quindi l'interfaccia dovrebbe gestirlo.
export interface IGuardService {
checkRole: (roles: ROLE[]) => boolean;
}
Ora prende l'array di RUOLO
tipo generico. La nostra semplice implementazione cambierà di poco.
class SimpleGuard implements IGuardService {
checkRole(roles: string[]): boolean {
let found: string | undefined = roles.find(e => e === currentUser.role);
return found !== undefined;
}
}
Abbiamo bisogno di aggiungere un parametro di tipo, ma potremmo preparare un'implementazione che supporti il parametro IRole
interfaccia.
interfaccia IRole {
nome: stringa;
}
//...
class RoleGuardService implementa IGuardService {
checkRole(roles: IRole[]): boolean {
let found: IRole | undefined = roles.find(e => e === userWithRole.role);
return found !== undefined;
}
}
```
Il secondo passo consiste nel propagare questa modifica a Oggetti IGuard
.
interfaccia IGuardProps estende React.PropsWithChildren {
requiredRoles: ROLE[];
guardService: IGuardService;
forbidden?: () => React.ReactNode;
}
E rispettivamente a Guardia
componente.
export const Guard = (props: IGuardProps) => {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} else if (props.forbidden === undefined) {
restituisce ;
} else {
return (
{props.forbidden()}
);
}
}
E i nostri casi d'uso
export const AppHeader: FC = () => {
const guardService = new SimpleGuard();
const roleService = new RoleGuardService();
return (
<header>
<nav>
<ul>
<Guard<IRole> requiredRoles={[{nome: "ADMIN"}]} guardService={roleService}>.
<li>Componente solo per l'amministratore</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>.
<li>Componente solo per l'utente</li>
</Guard>
<Guard<string> requiredRoles={["MODERATOR"]} guardService={guardService}
forbidden={() => <div>Vietato - solo il moderatore può vederlo</div>}>
<li>Componente moderatore</li>
</Guard>
<Guard<string> requiredRoles={["USER", "ADMIN"]} guardService={guardService}>.
<li>Componente amministratore e utente</li>
</Guard>
<li>Elemento di uso generale</li>
</ul>
</nav>
</header>
);
}
In questo esempio si utilizzano entrambe le implementazioni di IGuardservice
solo a scopo illustrativo. Nei casi d'uso reali si
probabilmente ne utilizzerà solo uno.
Il Guardia
possono essere annidati. Si ricordi che gli accessi saranno risolti in ordine dalla maggior parte delle istanze esterne.
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>Componente solo per l'amministratore</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>.
<li>Componente solo per l'utente</li>
</Guard>
<li>Componente amministratore e utente</li>
<Guard<string> requiredRoles={["MODERATOR"]} guardService={guardService}
forbidden={() => <div>Vietato - solo il moderatore può vederlo</div>}>
<li>Componente moderatore</li>
</Guard>
</Guard>
<li>Elemento di uso generale</li>
</ul>
</nav>
</header>
);
}
Nell'esempio precedente Componente moderatore
non potrebbe mai apparire, perché l'utente può gestire un solo ruolo. Prima Guardia
limiti
ruoli a AMMINISTRAZIONE
e UTENTE
, quindi MODERATORE
non passerà mai il primo controllo.
Possiamo costruire componenti specializzati che nascondono alcune proprietà.
export const AdminGuard = (props: Omit) => {
return
{props.children}
}
//...
export const SpecializedAppHeader: FC = () => {
const guardService = new SimpleGuard();
return (
Componente solo per l'amministratore
Componente per soli utenti
Vietato - solo il moderatore può vederlo
}>
Componente Moderatore
Componente Amministratore e Utente
Elemento di uso generale
);
}
```
In questo caso AdminGuard
predefinire AMMINISTRAZIONE
ruolo. Nelle conseguenze, dobbiamo definire esplicitamente il tipo di RUOLO
tipo
parametro.
In questo articolo vi mostrerò come creare e usare Guardia
componente in React. Partiamo da un codice complesso e difficile da
leggere e mantenere. Lo facciamo evolvere in uno stato più facile per gli sviluppatori e introduciamo un componente funzionale personalizzato. Successivamente
estendere il componente aggiungendo funzionalità extra, rifattorizzare il servizio di estrazione e infine aggiungere tipi generici.
Infine, abbiamo un componente che può essere annidato e che è facile da testare e mantenere.