Ventajas e inconvenientes del React
¿Por qué merece la pena utilizar React? ¿Qué ventajas tiene esta librería JavaScript? Para conocer las respuestas sumérjase en este artículo y descubra las ventajas reales de utilizar React.
Aprenda a simplificar y mejorar la visibilidad de los componentes en React utilizando la renderización condicional y los componentes de guardia.
Hoy me gustaría hablar de cómo controlar visibilidad de los componentes en React. Pero antes de empezar hay pequeñas
descargo de responsabilidad:
La solución presentada no es muy segura en el sentido de proteger su aplicación contra los "hackers" (quienesquiera que sean).
Recuerde que necesita proteger sus puntos finales y utilizar buenas prácticas en el diseño de aplicaciones. Esta solución sólo
hace su trabajo un poco más fácil.
Una de las funcionalidades más comunes es mostrar el componente sólo para un grupo de usuarios que tienen algunos derechos específicos,
roles o privilegios. La solución habitual es añadir si
s a código, comprobar manualmente las condiciones y mostrar o no los elementos.
Echemos un vistazo a SimpleAppHeader
que contiene pocos elementos de navegación.
<!-- wp:paragraph -->
<p><code>typescript jsx<br>
export const SimpleAppHeader: FC = () => {<br>
return (<br>
<br>
<nav><br>
<ul><br>
{<br>
currentUser.role === "ADMIN" &&<br>
<li>Componente sólo para administradores</li><br>
}<br>
{<br>
currentUser.role === "USER" &&<br>
<li>Componente sólo usuario</li><br>
}<br>
{<br>
(currentUser.role === "ADMIN" || currentUser.role === "USER") &&<br>
<li>Componente sólo para administradores y usuarios</li><br>
<br>
<li>Elemento de uso general</li><br>
</ul><br>
</nav><br>
</header><br>
)<br>
}<br>
</code></p>
<!-- /wp:paragraph -->
No está mal. Probablemente se podrían encapsular las comprobaciones en funciones para reducir la complejidad. usuarioactual
es algún objeto
que tiene papel
pero puede ser cualquier cosa que queramos utilizar como objeto de nuestro mecanismo de control.
Este código tiene algunos problemas. Si proyecto crece probablemente utilizamos esas construcciones en muchos lugares. Así que tenemos que copiar,
de alguna manera, las condiciones. Este código es difícil de mantener y cambiar en el futuro. Especialmente cuando las reglas de acceso cambian durante
tiempo p. ej. tenemos que cambiar usuarioactual
en otra cosa. Es muy difícil de probar. Tienes que escribir muchas pruebas
sólo para verificar si su estado es correcto.
Es hora de simplificar un poco este código. Podríamos extraer algunos métodos y hacer el código más corto y menos complejo:
const isAdmin = (rol: cadena): booleano => rol === "ADMIN";
const isUser = (rol: string): booleano => rol === "USUARIO";
const isAdminOrUser = (función: cadena): booleano => isAdmin(función) || isUser(función);
export const SimplifiedAppHeader: FC = () => {
return (
{
isAdmin(rolUsuarioactual) &&
Componente sólo admin
}
{
isUser(rolUsuarioactual) &&
Componente sólo usuario
}
{
(isAdminOrUser(rolUsuarioactual)) &&
Componente sólo Admin y Usuario
}
Elemento de uso general
)
}
```
Parece prometedor. Reducimos el ruido de las líneas repetidas. El código es más legible y fácil de mantener. Pero eche un vistazo a
función isAdminOrUser
. Sólo tenemos dos funciones. Si introducimos un tercer rol necesitamos crear otro conjunto de funciones
que combina papeles. Hora de otra versión.
Introduzcamos la función hasRole
que será el reemplazo de nuestro isX
funciones.
const hasRole = (rol: string, requiredRole: string[]): boolean => {
let found: string | undefined = requiredRole.find(s => rol === s);
return encontrado !== indefinido;
}
export const FilteringAppHeader: FC = () => {
return (
{
hasRole(usuarioactual.role, ["ADMIN"]) &&
Componente sólo para administradores
}
{
hasRole(rolUsuarioactual, ["USUARIO"]) &&
Componente sólo usuario
}
{
hasRole(usuarioactual.role, ["ADMIN", "USUARIO"]) &&
Componente sólo para administradores y usuarios
}
Elemento de uso general
)
}
```
Ahora se ve bien. Todavía tenemos condiciones en la parte html del código, pero ahora podemos probar hasRole
función y confiar en que
se utilizará con el conjunto correcto de parámetros. Añadir o cambiar roles es ahora más fácil. Usamos un array que contiene todos los
funciones que necesitamos.
Sin embargo, esta solución se une a usuarioactual
objeto. Se supone que el objeto es de alguna manera global o de fácil acceso en
cualquier lugar de la aplicación. Así que podemos tratar de encapsular esto. Por supuesto, podemos moverlo a hasRole
función:
const hasRole = (requiredRole: string[]): boolean => {
let found: string | undefined = requiredRole.find(s => currentUser.role === s);
return found !== undefined;
}
Pero eso no nos da casi nada.
Guardia
ComponenteEs hora de crear un componente que encapsule toda la lógica. Lo llamé Guardia
y así es como quiero usarlo.
export const GuardedAppHeader: FC = () => {
return (
);
}
El componente necesita dos propiedades. En primer lugar niños
responsable de los contenidos vigilados. Segundo requiredRoles
que
manejar el conjunto de roles que nos da acceso.
Necesitamos una representación de esta estructura. Es muy sencillo. Extendemos el tipo React.AtrezzoConNiños
que tiene niños
propiedad. Por supuesto, puede añadir manualmente esa propiedad a su tipo y omitir la extensión.
interface IGuardProps extends React.PropsWithChildren {
requiredRoles: cadena[];
}
El componente en sí también es sencillo. Reutilizaremos hasRole
función aquí.
export const Guard = (props: IGuardProps) => {
const hasRole = (requiredRole: string[]): boolean => {
let found: string | undefined = requiredRole.find(s => currentUser.role === s);
return encontrado !== indefinido;
}
if (hasRole(props.requiredRoles)) {
return (
{props.children}
);
} else {
return ;
}
}
"`
Y podría decir basta aquí, pero sería demasiado fácil. Ahora tenemos componente, y podemos usarlo hasta el extremo 😀 .
El primer paso será la externalización de los controles. usuarioactual
es un valor codificado. Me gustaría encapsular este valor
en algún servicio, que "sabrá" cómo verificar los roles. Técnicamente eso significa que movemos hasRole
a otra función
clase.
Creo una interfaz sencilla IGuardService
que sólo tiene una propiedad - hasRole
.
exportar interfaz IGuardService {
checkRole: (roles: string[]) => boolean;
}
Una aplicación sencilla podría ser la siguiente
class SimpleGuard implements IGuardService {
checkRole(roles: cadena[]): booleano {
let found: string | undefined = roles.find(e => e === currentUser.role);
return found !== undefined;
}
}
Para utilizarlo debemos cambiar IGuardProperties
y caso de uso.
export interface IGuardProps extends React.PropsWithChildren {
requiredRoles: cadena[];
guardService: IGuardService;
}
// ...
const AppHeader: FC = () => {
const guardService = nuevo SimpleGuard();
return (
Componente sólo para administradores
Componente sólo usuario
Componente Admin y Usuario
Elemento de uso general
);
}
```
Ahora el componente parece:
export const Guard = (props: IGuardProps) => {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} else {
return ;
}
}
Mucho mejor. Servicio de Guardia
nos separan de la lógica que controla los papeles. Podemos cambiarlo sin consecuencias para nuestro
componente. El caso de uso común es utilizar un simulacro en las pruebas y una implementación "real" en el código de producción.
La próxima mejora será la gestión de los Prohibido
elemento. La solución actual muestra un elemento vacío. Primero necesitamos
cambiar IGuardProps
función de adición que se renderizará ese elemento.
export interface IGuardProps extends React.PropsWithChildren {
requiredRoles: cadena[];
guardService: IGuardService;
¿prohibido?: () => React.ReactNode;
}
Esta propiedad es opcional, el nombre termina con un signo de interrogación. Así que podría ser función o indefinido
. Necesitamos
manejarlo en Guardia
componente.
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 (
Componente sólo para administradores
Componente sólo usuario
Prohibido - sólo moderador puede ver esto
}>
Componente moderador
Componente administrador y usuario
Elemento de uso general
);
}
```
Es hora del último gran cambio. El componente de la versión actual sólo admite roles como cadena
. Podríamos tener múltiples
tipos de propiedad que nos gustaría comprobar. Números o tipos personalizados no es nada especial. Añadiré soporte para genéricos.
El primer paso son los cambios en IGuardService
interfaz. Las implementaciones dependerán del tipo de valor comprobado. Puede ser
ser cualquier cosa, por lo que la interfaz debe manejarlo.
exportar interfaz IGuardService {
checkRole: (roles: ROLE[]) => boolean;
}
Ahora toma array de PAPEL
tipo genérico. Nuestra implementación simple cambiará un poco.
class SimpleGuard implements IGuardService {
checkRole(roles: string[]): boolean {
let found: string | undefined = roles.find(e => e === currentUser.role);
return found !== undefined;
}
}
Necesitamos añadir un parámetro de tipo, pero podríamos preparar una implementación que admita IRole
interfaz.
interfaz IRole {
nombre: cadena;
}
//...
class RoleGuardService implements IGuardService {
checkRole(roles: IRole[]): boolean {
let encontrado: IRole | undefined = roles.find(e => e === userWithRole.role);
return encontrado !== indefinido;
}
}
```
El segundo paso es propagar este cambio a IGuardProps
.
interface IGuardProps extends React.PropsWithChildren {
requiredRoles: ROLE[];
guardService: IGuardService;
¿prohibido?: () => React.ReactNode;
}
Y respectivamente a Guardia
componente.
export const Guard = (props: IGuardProps) => {
if (props.guardService.checkRole(props.requiredRoles)) {
return (
{props.children}
);
} else if (props.forbidden === undefined) {
return ;
} else {
return (
{props.forbidden()}
);
}
}
Y nuestros casos de uso
export const AppHeader: FC = () => {
const guardService = nuevo SimpleGuard();
const roleService = nuevo RoleGuardService();
return (
<header>
<nav>
<ul>
<Guard<IRole> requiredRoles={[{name: "ADMIN"}]} guardService={roleService}>
<li>Componente sólo para administradores</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>
<li>Componente sólo para usuarios</li>
</Guard>
<Guard<string> requiredRoles={["MODERADOR"]} guardService={guardService}
forbidden={() => <div>Prohibido - sólo moderador puede ver esto</div>}>
<li>Componente moderador</li>
</Guard>
<Guard<string> requiredRoles={["USER", "ADMIN"]} guardService={guardService}>
<li>Componente Admin y Usuario</li>
</Guard>
<li>Elemento de uso general</li>
</ul>
</nav>
</header>
);
}
En este ejemplo utilizo ambas implementaciones de IGuardservice
sólo con fines ilustrativos. En casos reales
probablemente utilice sólo uno.
En Guardia
pueden anidarse. Sólo recuerde que el acceso se resolverá en orden desde la instancia más externa.
export const NestAppHeader: FC = () => {
const guardService = nuevo SimpleGuard();
return (
<header>
<nav>
<ul>
<Guard<string> requiredRoles={["USER", "ADMIN"]} guardService={guardService}>
<Guard<string> requiredRoles={["ADMIN"]} guardService={guardService}>
<li>Componente sólo para administradores</li>
</Guard>
<Guard<string> requiredRoles={["USER"]} guardService={guardService}>
<li>Componente sólo para usuarios</li>
</Guard>
<li>Componente Admin y Usuario</li>
<Guard<string> requiredRoles={["MODERADOR"]} guardService={guardService}
forbidden={() => <div>Prohibido - sólo moderador puede ver esto</div>}>
<li>Componente moderador</li>
</Guard>
</Guard>
<li>Elemento de uso general</li>
</ul>
</nav>
</header>
);
}
En el ejemplo anterior Componente moderador
nunca podría aparecer, porque el usuario sólo puede manejar un rol. Primero Guardia
límites
funciones a ADMIN
y USUARIO
Así que MODERADOR
nunca pasará el primer control.
Podemos construir componentes especializados que oculten algunas propiedades.
export const AdminGuard = (props: Omit) => {
return
{props.children}
}
//...
export const SpecializedAppHeader: FC = () => {
const guardService = new SimpleGuard();
return (
Componente sólo para administradores
Componente sólo para usuarios
Prohibido - sólo moderador puede ver esto
}>
Componente moderador
Componente de administrador y usuario
Elemento de uso general
);
}
```
En este caso AdminGuard
predefinir ADMIN
papel. En consecuencia, necesitamos definir explícitamente el tipo de PAPEL
tipo
parámetro.
En este artículo te muestro cómo crear y utilizar Guardia
componente en React. Partimos de un código complejo y difícil de
leer y mantener. Lo convertimos en un estado más fácil de desarrollar e introducimos componentes funcionales personalizados. A continuación
ampliar el componente añadiendo funcionalidades adicionales, refactorizar el servicio de extracción y, por último, añadir tipos genéricos.
Por último, tenemos componente que se puede anidar es fácil de probar y mantener.